• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1+++
2title = "ASIO/Networking TS: Boost >= 1.70"
3description = "How to teach ASIO/Networking TS about Outcome."
4tags = [ "asio", "networking-ts" ]
5+++
6
7*Thanks to [Christos Stratopoulos](https://github.com/cstratopoulos) for this Outcome recipe.*
8
9---
10
11### Compatibility note
12
13This recipe targets Boost versions including and after 1.70, where coroutine support is
14based around the `asio::use_awaitable` completion token. For integration with Boost versions
15before 1.70, see [this recipe](asio-integration).
16
17---
18
19### Use case
20
21[Boost.ASIO](https://www.boost.org/doc/libs/develop/doc/html/boost_asio.html)
22and [standalone ASIO](https://think-async.com/Asio/) provide the
23[`async_result`](https://www.boost.org/doc/libs/develop/doc/html/boost_asio/reference/async_result.html)
24customisation point for adapting arbitrary third party libraries, such as Outcome, into ASIO.
25
26Historically in ASIO you need to pass completion handler instances
27to the ASIO asynchronous i/o initiation functions. These get executed when the i/o
28completes.
29
30{{% snippet "boost-only/asio_integration_1_70.cpp" "old-use-case" %}}
31
32One of the big value adds of the Coroutines TS is the ability to not have to write
33so much boilerplate if you have a Coroutines supporting compiler:
34
35{{% snippet "boost-only/asio_integration_1_70.cpp" "new-use-case" %}}
36
37The default ASIO implementation always throws exceptions on failure through
38its coroutine token transformation. The [`redirect_error`](https://www.boost.org/doc/libs/develop/doc/html/boost_asio/reference/experimental__redirect_error.html)
39token transformation recovers the option to use the `error_code` interface,
40but it suffers from the [same drawbacks]({{< relref "/motivation/error_codes" >}})
41that make pure error codes unappealing in the synchronous case.
42
43This recipe fixes that by making it possible for coroutinised
44i/o in ASIO to return a `result<T>`:
45
46{{% snippet "boost-only/asio_integration_1_70.cpp" "outcome-use-case" %}}
47
48---
49
50### Implementation
51
52{{% notice warning %}}
53The below involves a lot of ASIO voodoo. **NO SUPPORT WILL BE GIVEN HERE FOR THE ASIO
54CODE BELOW**. Please raise any questions or problems that you have with how to implement
55this sort of stuff in ASIO
56on [Stackoverflow #boost-asio](https://stackoverflow.com/questions/tagged/boost-asio).
57{{% /notice %}}
58
59The real world, production-level recipe can be found at the bottom of this page.
60You ought to use that in any real world use case.
61
62It is however worth providing a walkthrough of a simplified edition of the real world
63recipe, as a lot of barely documented ASIO voodoo is involved. You should not
64use the code presented next in your own code, it is too simplified. But it should
65help you understand how the real implementation works.
66
67Firstly we need to define some helper type sugar and a factory function for wrapping
68any arbitrary third party completion token with that type sugar:
69
70{{% snippet "boost-only/asio_integration_1_70.cpp" "as_result" %}}
71
72Next we tell ASIO about a new completion token it ought to recognise by specialising
73[`async_result`](https://www.boost.org/doc/libs/develop/doc/html/boost_asio/reference/async_result.html):
74
75{{% snippet "boost-only/asio_integration_1_70.cpp" "async_result1" %}}
76
77There are a couple tricky parts to understand. First of all, we want our
78`async_result` specialization to work, in particular, with the `async_result` for
79ASIO's
80[`use_awaitable_t` completion token](https://www.boost.org/doc/libs/develop/doc/html/boost_asio/reference/use_awaitable_t.html).
81With this token, the `async_result` specialization takes the form with a static
82`initiate` method which defers initiation of the asynchronous operation until,
83for example,
84`co_await` is called on the returned `awaitable`. Thus, our `async_result`
85specialization will take the same form. With this in mind, we need only
86understand how our specialization will implement its `initiate` method. The trick
87is that it will pass the initiation work off to an `async_result` for the
88supplied completion token type with a completion handler which consumes `result<T>`.
89Our `async_result` is thus just a simple wrapper over this underlying
90`async_result`, but we inject a completion handler with the
91`void(error_code, size_t)` signature which constructs from that a `result`:
92
93{{% snippet "boost-only/asio_integration_1_70.cpp" "async_result2" %}}
94
95To use, simply wrap the third party completion token with `as_result` to cause
96ASIO to return from `co_await` a `result` instead of throwing exceptions on
97failure:
98
99```c++
100char buffer[1024];
101
102outcome::result<size_t, error_code> bytesread =
103  co_await skt.async_read_some(asio::buffer(buffer), as_result(asio::use_awaitable));
104```
105
106The real world production-level implementation below is a lot more complex than the
107above which has been deliberately simplified to aid exposition. The above
108should help you get up and running with the below, eventually.
109
110One again I would like to remind you that Outcome is not the appropriate place
111to seek help with ASIO voodoo. Please ask on
112[Stackoverflow #boost-asio](https://stackoverflow.com/questions/tagged/boost-asio).
113
114---
115
116Here follows the real world, production-level adapation of Outcome into
117ASIO, written and maintained by [Christos Stratopoulos](https://github.com/cstratopoulos).
118If the following does not load due to Javascript being disabled, you can visit the gist at
119https://gist.github.com/cstratopoulos/901b5cdd41d07c6ce6d83798b09ecf9b/863c1dbf3b063a5ff9ff2bdd834242ead556e74e.
120
121
122
123{{% gist "cstratopoulos" "901b5cdd41d07c6ce6d83798b09ecf9b/863c1dbf3b063a5ff9ff2bdd834242ead556e74e" %}}
124