include/boost/corosio/tcp_socket.hpp

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