include/boost/corosio/tcp_socket.hpp

95.5% Lines (42/0/44) 100.0% List of functions (28/0/28)
tcp_socket.hpp
f(x) Functions (28)
Function Calls Lines Blocks
boost::corosio::tcp_socket::connect_awaitable::connect_awaitable(boost::corosio::tcp_socket&, boost::corosio::endpoint) :202 4374x 100.0% 100.0% boost::corosio::tcp_socket::connect_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :205 4374x 100.0% 80.0% boost::corosio::tcp_socket::wait_awaitable::wait_awaitable(boost::corosio::tcp_socket&, boost::corosio::wait_type) :219 37x 100.0% 100.0% boost::corosio::tcp_socket::wait_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :222 37x 100.0% 80.0% boost::corosio::tcp_socket::tcp_socket<boost::corosio::io_context::executor_type>(boost::corosio::io_context::executor_type const&) :251 1x 100.0% 100.0% boost::corosio::tcp_socket::tcp_socket(boost::corosio::tcp_socket&&) :267 490x 100.0% 100.0% boost::corosio::tcp_socket::operator=(boost::corosio::tcp_socket&&) :284 23x 100.0% 100.0% boost::corosio::tcp_socket::is_open() const :345 27710x 100.0% 100.0% boost::corosio::tcp_socket::connect(boost::corosio::endpoint) :389 4374x 100.0% 100.0% boost::corosio::tcp_socket::wait(boost::corosio::wait_type) :420 37x 100.0% 100.0% void boost::corosio::tcp_socket::set_option<boost::corosio::native_socket_option::boolean<6, 1> >(boost::corosio::native_socket_option::boolean<6, 1> const&) :546 4x 71.4% 86.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::keep_alive>(boost::corosio::socket_option::keep_alive const&) :546 10x 71.4% 86.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::linger>(boost::corosio::socket_option::linger const&) :546 152x 71.4% 86.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::no_delay>(boost::corosio::socket_option::no_delay const&) :546 27x 85.7% 93.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::receive_buffer_size>(boost::corosio::socket_option::receive_buffer_size const&) :546 10x 71.4% 86.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::reuse_address>(boost::corosio::socket_option::reuse_address const&) :546 2x 71.4% 86.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :546 6x 71.4% 86.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::v6_only>(boost::corosio::socket_option::v6_only const&) :546 6x 71.4% 86.0% boost::corosio::native_socket_option::boolean<6, 1> boost::corosio::tcp_socket::get_option<boost::corosio::native_socket_option::boolean<6, 1> >() const :573 4x 80.0% 88.0% boost::corosio::socket_option::keep_alive boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::keep_alive>() const :573 10x 80.0% 88.0% boost::corosio::socket_option::linger boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::linger>() const :573 12x 80.0% 88.0% boost::corosio::socket_option::no_delay boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::no_delay>() const :573 29x 90.0% 94.0% boost::corosio::socket_option::receive_buffer_size boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::receive_buffer_size>() const :573 12x 80.0% 88.0% boost::corosio::socket_option::reuse_address boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::reuse_address>() const :573 2x 80.0% 88.0% boost::corosio::socket_option::send_buffer_size boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::send_buffer_size>() const :573 8x 80.0% 88.0% boost::corosio::socket_option::v6_only boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::v6_only>() const :573 6x 80.0% 88.0% boost::corosio::tcp_socket::tcp_socket() :621 31x 100.0% 100.0% boost::corosio::tcp_socket::get() const :631 32256x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_TCP_SOCKET_HPP
12 #define BOOST_COROSIO_TCP_SOCKET_HPP
13
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/detail/platform.hpp>
16 #include <boost/corosio/detail/except.hpp>
17 #include <boost/corosio/detail/native_handle.hpp>
18 #include <boost/corosio/detail/op_base.hpp>
19 #include <boost/corosio/io/io_stream.hpp>
20 #include <boost/capy/io_result.hpp>
21 #include <boost/corosio/detail/buffer_param.hpp>
22 #include <boost/corosio/endpoint.hpp>
23 #include <boost/corosio/shutdown_type.hpp>
24 #include <boost/corosio/tcp.hpp>
25 #include <boost/corosio/wait_type.hpp>
26 #include <boost/capy/ex/executor_ref.hpp>
27 #include <boost/capy/ex/execution_context.hpp>
28 #include <boost/capy/ex/io_env.hpp>
29 #include <boost/capy/concept/executor.hpp>
30
31 #include <system_error>
32
33 #include <concepts>
34 #include <coroutine>
35 #include <cstddef>
36 #include <stop_token>
37 #include <type_traits>
38
39 namespace boost::corosio {
40
41 /** An asynchronous TCP socket for coroutine I/O.
42
43 This class provides asynchronous TCP socket operations that return
44 awaitable types. Each operation participates in the affine awaitable
45 protocol, ensuring coroutines resume on the correct executor.
46
47 The socket must be opened before performing I/O operations. Operations
48 support cancellation through `std::stop_token` via the affine protocol,
49 or explicitly through the `cancel()` member function.
50
51 @par Thread Safety
52 Distinct objects: Safe.@n
53 Shared objects: Unsafe. A socket must not have concurrent operations
54 of the same type (e.g., two simultaneous reads). One read and one
55 write may be in flight simultaneously.
56
57 @par Semantics
58 Wraps the platform TCP/IP stack. Operations dispatch to
59 OS socket APIs via the io_context reactor (epoll, IOCP,
60 kqueue). Satisfies @ref capy::Stream.
61
62 @par Example
63 @code
64 io_context ioc;
65 tcp_socket s(ioc);
66 s.open();
67
68 // Using structured bindings
69 auto [ec] = co_await s.connect(
70 endpoint(ipv4_address::loopback(), 8080));
71 if (ec)
72 co_return;
73
74 char buf[1024];
75 auto [read_ec, n] = co_await s.read_some(
76 capy::mutable_buffer(buf, sizeof(buf)));
77 @endcode
78 */
79 class BOOST_COROSIO_DECL tcp_socket : public io_stream
80 {
81 public:
82 /// The endpoint type used by this socket.
83 using endpoint_type = corosio::endpoint;
84
85 using shutdown_type = corosio::shutdown_type;
86 using enum corosio::shutdown_type;
87
88 /** Define backend hooks for TCP socket operations.
89
90 Platform backends (epoll, IOCP, kqueue, select) derive from
91 this to implement socket I/O, connection, and option management.
92 */
93 struct implementation : io_stream::implementation
94 {
95 /** Initiate an asynchronous connect to the given endpoint.
96
97 @param h Coroutine handle to resume on completion.
98 @param ex Executor for dispatching the completion.
99 @param ep The remote endpoint to connect to.
100 @param token Stop token for cancellation.
101 @param ec Output error code.
102
103 @return Coroutine handle to resume immediately.
104 */
105 virtual std::coroutine_handle<> connect(
106 std::coroutine_handle<> h,
107 capy::executor_ref ex,
108 endpoint ep,
109 std::stop_token token,
110 std::error_code* ec) = 0;
111
112 /** Initiate an asynchronous wait for socket readiness.
113
114 Completes when the socket becomes ready for the
115 specified direction, or an error condition is
116 reported. No bytes are transferred.
117
118 @param h Coroutine handle to resume on completion.
119 @param ex Executor for dispatching the completion.
120 @param w The direction to wait on.
121 @param token Stop token for cancellation.
122 @param ec Output error code.
123
124 @return Coroutine handle to resume immediately.
125 */
126 virtual std::coroutine_handle<> wait(
127 std::coroutine_handle<> h,
128 capy::executor_ref ex,
129 wait_type w,
130 std::stop_token token,
131 std::error_code* ec) = 0;
132
133 /** Shut down the socket for the given direction(s).
134
135 @param what The shutdown direction.
136
137 @return Error code on failure, empty on success.
138 */
139 virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
140
141 /// Return the platform socket descriptor.
142 virtual native_handle_type native_handle() const noexcept = 0;
143
144 /** Release ownership of the native socket handle.
145
146 Deregisters the socket from the backend and cancels
147 pending operations without closing the descriptor. The
148 caller takes ownership.
149
150 @return The native handle.
151 */
152 virtual native_handle_type release_socket() noexcept = 0;
153
154 /** Request cancellation of pending asynchronous operations.
155
156 All outstanding operations complete with operation_canceled error.
157 Check `ec == cond::canceled` for portable comparison.
158 */
159 virtual void cancel() noexcept = 0;
160
161 /** Set a socket option.
162
163 @param level The protocol level (e.g. `SOL_SOCKET`).
164 @param optname The option name (e.g. `SO_KEEPALIVE`).
165 @param data Pointer to the option value.
166 @param size Size of the option value in bytes.
167 @return Error code on failure, empty on success.
168 */
169 virtual std::error_code set_option(
170 int level,
171 int optname,
172 void const* data,
173 std::size_t size) noexcept = 0;
174
175 /** Get a socket option.
176
177 @param level The protocol level (e.g. `SOL_SOCKET`).
178 @param optname The option name (e.g. `SO_KEEPALIVE`).
179 @param data Pointer to receive the option value.
180 @param size On entry, the size of the buffer. On exit,
181 the size of the option value.
182 @return Error code on failure, empty on success.
183 */
184 virtual std::error_code
185 get_option(int level, int optname, void* data, std::size_t* size)
186 const noexcept = 0;
187
188 /// Return the cached local endpoint.
189 virtual endpoint local_endpoint() const noexcept = 0;
190
191 /// Return the cached remote endpoint.
192 virtual endpoint remote_endpoint() const noexcept = 0;
193 };
194
195 /// Represent the awaitable returned by @ref connect.
196 struct connect_awaitable
197 : detail::void_op_base<connect_awaitable>
198 {
199 tcp_socket& s_;
200 endpoint endpoint_;
201
202 4374x connect_awaitable(tcp_socket& s, endpoint ep) noexcept
203 4374x : s_(s), endpoint_(ep) {}
204
205 4374x std::coroutine_handle<> dispatch(
206 std::coroutine_handle<> h, capy::executor_ref ex) const
207 {
208 4374x return s_.get().connect(h, ex, endpoint_, token_, &ec_);
209 }
210 };
211
212 /// Represent the awaitable returned by @ref wait.
213 struct wait_awaitable
214 : detail::void_op_base<wait_awaitable>
215 {
216 tcp_socket& s_;
217 wait_type w_;
218
219 37x wait_awaitable(tcp_socket& s, wait_type w) noexcept
220 37x : s_(s), w_(w) {}
221
222 37x std::coroutine_handle<> dispatch(
223 std::coroutine_handle<> h, capy::executor_ref ex) const
224 {
225 37x return s_.get().wait(h, ex, w_, token_, &ec_);
226 }
227 };
228
229 public:
230 /** Destructor.
231
232 Closes the socket if open, cancelling any pending operations.
233 */
234 ~tcp_socket() override;
235
236 /** Construct a socket from an execution context.
237
238 @param ctx The execution context that will own this socket.
239 */
240 explicit tcp_socket(capy::execution_context& ctx);
241
242 /** Construct a socket from an executor.
243
244 The socket is associated with the executor's context.
245
246 @param ex The executor whose context will own the socket.
247 */
248 template<class Ex>
249 requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
250 capy::Executor<Ex>
251 1x explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
252 {
253 1x }
254
255 /** Move constructor.
256
257 Transfers ownership of the socket resources.
258
259 @param other The socket to move from.
260
261 @pre No awaitables returned by @p other's methods exist.
262 @pre @p other is not referenced as a peer in any outstanding
263 accept awaitable.
264 @pre The execution context associated with @p other must
265 outlive this socket.
266 */
267 490x tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
268
269 /** Move assignment operator.
270
271 Closes any existing socket and transfers ownership.
272
273 @param other The socket to move from.
274
275 @pre No awaitables returned by either `*this` or @p other's
276 methods exist.
277 @pre Neither `*this` nor @p other is referenced as a peer in
278 any outstanding accept awaitable.
279 @pre The execution context associated with @p other must
280 outlive this socket.
281
282 @return Reference to this socket.
283 */
284 23x tcp_socket& operator=(tcp_socket&& other) noexcept
285 {
286 23x if (this != &other)
287 {
288 23x close();
289 23x h_ = std::move(other.h_);
290 }
291 23x return *this;
292 }
293
294 tcp_socket(tcp_socket const&) = delete;
295 tcp_socket& operator=(tcp_socket const&) = delete;
296
297 /** Open the socket.
298
299 Creates a TCP socket and associates it with the platform
300 reactor (IOCP on Windows). Calling @ref connect on a closed
301 socket opens it automatically with the endpoint's address family,
302 so explicit `open()` is only needed when socket options must be
303 set before connecting.
304
305 @param proto The protocol (IPv4 or IPv6). Defaults to
306 `tcp::v4()`.
307
308 @throws std::system_error on failure.
309 */
310 void open(tcp proto = tcp::v4());
311
312 /** Bind the socket to a local endpoint.
313
314 Associates the socket with a local address and port before
315 connecting. Useful for multi-homed hosts or source-port
316 pinning.
317
318 @param ep The local endpoint to bind to.
319
320 @return An error code indicating success or the reason for
321 failure.
322
323 @par Error Conditions
324 @li `errc::address_in_use`: The endpoint is already in use.
325 @li `errc::address_not_available`: The address is not
326 available on any local interface.
327 @li `errc::permission_denied`: Insufficient privileges to
328 bind to the endpoint (e.g., privileged port).
329
330 @throws std::logic_error if the socket is not open.
331 */
332 [[nodiscard]] std::error_code bind(endpoint ep);
333
334 /** Close the socket.
335
336 Releases socket resources. Any pending operations complete
337 with `errc::operation_canceled`.
338 */
339 void close();
340
341 /** Check if the socket is open.
342
343 @return `true` if the socket is open and ready for operations.
344 */
345 27710x bool is_open() const noexcept
346 {
347 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
348 return h_ && get().native_handle() != ~native_handle_type(0);
349 #else
350 27710x return h_ && get().native_handle() >= 0;
351 #endif
352 }
353
354 /** Initiate an asynchronous connect operation.
355
356 If the socket is not already open, it is opened automatically
357 using the address family of @p ep (IPv4 or IPv6). If the socket
358 is already open, the existing file descriptor is used as-is.
359
360 The operation supports cancellation via `std::stop_token` through
361 the affine awaitable protocol. If the associated stop token is
362 triggered, the operation completes immediately with
363 `errc::operation_canceled`.
364
365 @param ep The remote endpoint to connect to.
366
367 @return An awaitable that completes with `io_result<>`.
368 Returns success (default error_code) on successful connection,
369 or an error code on failure including:
370 - connection_refused: No server listening at endpoint
371 - timed_out: Connection attempt timed out
372 - network_unreachable: No route to host
373 - operation_canceled: Cancelled via stop_token or cancel().
374 Check `ec == cond::canceled` for portable comparison.
375
376 @throws std::system_error if the socket needs to be opened
377 and the open fails.
378
379 @par Preconditions
380 This socket must outlive the returned awaitable.
381
382 @par Example
383 @code
384 // Socket opened automatically with correct address family:
385 auto [ec] = co_await s.connect(endpoint);
386 if (ec) { ... }
387 @endcode
388 */
389 4374x auto connect(endpoint ep)
390 {
391 4374x if (!is_open())
392 54x open(ep.is_v6() ? tcp::v6() : tcp::v4());
393 4374x return connect_awaitable(*this, ep);
394 }
395
396 /** Wait for the socket to become ready in a given direction.
397
398 Suspends until the socket is ready for the requested
399 direction, or an error condition is reported. No bytes
400 are transferred — useful for integrating with C libraries
401 that own the I/O on a nonblocking fd and only need
402 readiness notification (e.g. libpq async, libssh).
403
404 The operation supports cancellation via `std::stop_token`
405 through the affine awaitable protocol. If the associated
406 stop token is triggered, the operation completes
407 immediately with `errc::operation_canceled`.
408
409 @param w The wait direction (read, write, or error).
410
411 @return An awaitable that completes with `io_result<>`.
412 On success, no bytes have been consumed from the
413 stream; a subsequent `read_some` (for read waits)
414 returns the available data.
415
416 @par Preconditions
417 The socket must be open. This socket must outlive the
418 returned awaitable.
419 */
420 37x [[nodiscard]] auto wait(wait_type w)
421 {
422 37x return wait_awaitable(*this, w);
423 }
424
425 /** Cancel any pending asynchronous operations.
426
427 All outstanding operations complete with `errc::operation_canceled`.
428 Check `ec == cond::canceled` for portable comparison.
429 */
430 void cancel();
431
432 /** Get the native socket handle.
433
434 Returns the underlying platform-specific socket descriptor.
435 On POSIX systems this is an `int` file descriptor.
436 On Windows this is a `SOCKET` handle.
437
438 @return The native socket handle, or -1/INVALID_SOCKET if not open.
439
440 @par Preconditions
441 None. May be called on closed sockets.
442 */
443 native_handle_type native_handle() const noexcept;
444
445 /** Assign an existing native socket to this object.
446
447 Adopts a TCP socket created outside the library — received
448 from another process, inherited, or made natively — and
449 registers it with the backend. The socket must be a stream
450 socket in the `AF_INET` or `AF_INET6` family. Adoption never
451 alters the descriptor's flags or options: on POSIX the fd
452 must already be non-blocking, and on Windows the socket must
453 be overlapped-capable.
454
455 If this object is already open, pending operations complete
456 with `errc::operation_canceled` and the held socket is
457 closed before the new one is adopted.
458
459 @par Exception Safety
460 Strong guarantee on validation failure: the object is
461 unchanged. If backend registration fails, the object either
462 retains its previous socket or is left closed, depending on
463 the backend. In all failure cases the caller retains
464 ownership of `fd`.
465
466 @param fd The native socket to adopt. On success the object
467 owns it and will close it.
468
469 @throws std::system_error On validation or registration
470 failure.
471 */
472 void assign(native_handle_type fd);
473
474 /** Release ownership of the native socket handle.
475
476 Deregisters the socket from the backend and cancels pending
477 operations without closing the descriptor. The caller takes
478 ownership of the returned handle.
479
480 @return The native handle.
481
482 @throws std::logic_error if the socket is not open.
483
484 @post is_open() == false
485 */
486 native_handle_type release();
487
488 /** Disable sends or receives on the socket.
489
490 TCP connections are full-duplex: each direction (send and receive)
491 operates independently. This function allows you to close one or
492 both directions without destroying the socket.
493
494 @li @ref shutdown_send sends a TCP FIN packet to the peer,
495 signaling that you have no more data to send. You can still
496 receive data until the peer also closes their send direction.
497 This is the most common use case, typically called before
498 close() to ensure graceful connection termination.
499
500 @li @ref shutdown_receive disables reading on the socket. This
501 does NOT send anything to the peer - they are not informed
502 and may continue sending data. Subsequent reads will fail
503 or return end-of-file. Incoming data may be discarded or
504 buffered depending on the operating system.
505
506 @li @ref shutdown_both combines both effects: sends a FIN and
507 disables reading.
508
509 When the peer shuts down their send direction (sends a FIN),
510 subsequent read operations will complete with `capy::cond::eof`.
511 Use the portable condition test rather than comparing error
512 codes directly:
513
514 @code
515 auto [ec, n] = co_await sock.read_some(buffer);
516 if (ec == capy::cond::eof)
517 {
518 // Peer closed their send direction
519 }
520 @endcode
521
522 Any error from the underlying system call is silently discarded
523 because it is unlikely to be helpful.
524
525 @param what Determines what operations will no longer be allowed.
526 */
527 void shutdown(shutdown_type what);
528
529 /** Set a socket option.
530
531 Applies a type-safe socket option to the underlying socket.
532 The option type encodes the protocol level and option name.
533
534 @par Example
535 @code
536 sock.set_option( socket_option::no_delay( true ) );
537 sock.set_option( socket_option::receive_buffer_size( 65536 ) );
538 @endcode
539
540 @param opt The option to set.
541
542 @throws std::logic_error if the socket is not open.
543 @throws std::system_error on failure.
544 */
545 template<class Option>
546 217x void set_option(Option const& opt)
547 {
548 217x if (!is_open())
549 2x detail::throw_logic_error("set_option: socket not open");
550 215x std::error_code ec = get().set_option(
551 Option::level(), Option::name(), opt.data(), opt.size());
552 215x if (ec)
553 detail::throw_system_error(ec, "tcp_socket::set_option");
554 215x }
555
556 /** Get a socket option.
557
558 Retrieves the current value of a type-safe socket option.
559
560 @par Example
561 @code
562 auto nd = sock.get_option<socket_option::no_delay>();
563 if ( nd.value() )
564 // Nagle's algorithm is disabled
565 @endcode
566
567 @return The current option value.
568
569 @throws std::logic_error if the socket is not open.
570 @throws std::system_error on failure.
571 */
572 template<class Option>
573 83x Option get_option() const
574 {
575 83x if (!is_open())
576 2x detail::throw_logic_error("get_option: socket not open");
577 81x Option opt{};
578 81x std::size_t sz = opt.size();
579 std::error_code ec =
580 81x get().get_option(Option::level(), Option::name(), opt.data(), &sz);
581 81x if (ec)
582 detail::throw_system_error(ec, "tcp_socket::get_option");
583 81x opt.resize(sz);
584 81x return opt;
585 }
586
587 /** Get the local endpoint of the socket.
588
589 Returns the local address and port to which the socket is bound.
590 For a connected socket, this is the local side of the connection.
591 The endpoint is cached when the connection is established.
592
593 @return The local endpoint, or a default endpoint (0.0.0.0:0) if
594 the socket is not connected.
595
596 @par Thread Safety
597 The cached endpoint value is set during connect/accept completion
598 and cleared during close(). This function may be called concurrently
599 with I/O operations, but must not be called concurrently with
600 connect(), accept(), or close().
601 */
602 endpoint local_endpoint() const noexcept;
603
604 /** Get the remote endpoint of the socket.
605
606 Returns the remote address and port to which the socket is connected.
607 The endpoint is cached when the connection is established.
608
609 @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
610 the socket is not connected.
611
612 @par Thread Safety
613 The cached endpoint value is set during connect/accept completion
614 and cleared during close(). This function may be called concurrently
615 with I/O operations, but must not be called concurrently with
616 connect(), accept(), or close().
617 */
618 endpoint remote_endpoint() const noexcept;
619
620 protected:
621 31x tcp_socket() noexcept = default;
622
623 explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
624
625 private:
626 friend class tcp_acceptor;
627
628 /// Open the socket for the given protocol triple.
629 void open_for_family(int family, int type, int protocol);
630
631 32256x inline implementation& get() const noexcept
632 {
633 32256x return *static_cast<implementation*>(h_.get());
634 }
635 };
636
637 } // namespace boost::corosio
638
639 #endif
640