100.00% Lines (92/92) 100.00% Functions (25/25)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP
13   13  
14   #include <boost/corosio/udp_socket.hpp> 14   #include <boost/corosio/udp_socket.hpp>
15   #include <boost/corosio/backend.hpp> 15   #include <boost/corosio/backend.hpp>
16   #include <boost/corosio/detail/op_base.hpp> 16   #include <boost/corosio/detail/op_base.hpp>
17   17  
18   #ifndef BOOST_COROSIO_MRDOCS 18   #ifndef BOOST_COROSIO_MRDOCS
19   #if BOOST_COROSIO_HAS_EPOLL 19   #if BOOST_COROSIO_HAS_EPOLL
20   #include <boost/corosio/native/detail/epoll/epoll_types.hpp> 20   #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
21   #endif 21   #endif
22   22  
23   #if BOOST_COROSIO_HAS_SELECT 23   #if BOOST_COROSIO_HAS_SELECT
24   #include <boost/corosio/native/detail/select/select_types.hpp> 24   #include <boost/corosio/native/detail/select/select_types.hpp>
25   #endif 25   #endif
26   26  
27   #if BOOST_COROSIO_HAS_KQUEUE 27   #if BOOST_COROSIO_HAS_KQUEUE
28   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp> 28   #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
29   #endif 29   #endif
30   30  
31   #if BOOST_COROSIO_HAS_URING 31   #if BOOST_COROSIO_HAS_URING
32   #include <boost/corosio/native/detail/uring/uring_types.hpp> 32   #include <boost/corosio/native/detail/uring/uring_types.hpp>
33   #endif 33   #endif
34   34  
35   #if BOOST_COROSIO_HAS_IOCP 35   #if BOOST_COROSIO_HAS_IOCP
36   #include <boost/corosio/native/detail/iocp/win_udp_service.hpp> 36   #include <boost/corosio/native/detail/iocp/win_udp_service.hpp>
37   #endif 37   #endif
38   #endif // !BOOST_COROSIO_MRDOCS 38   #endif // !BOOST_COROSIO_MRDOCS
39   39  
40   namespace boost::corosio { 40   namespace boost::corosio {
41   41  
42 - /** An asynchronous UDP socket with devirtualized I/O operations. 42 + /** Sends and receives UDP datagrams, calling the backend directly.
43   43  
44 - This class template inherits from @ref udp_socket and shadows 44 + This class template inherits from @ref udp_socket. It shadows the
45 - the async operations (`send_to`, `recv_from`, `connect`, `send`, 45 + async operations (`send_to`, `recv_from`, `connect`, `send`, `recv`)
46 - `recv`) with versions that call the backend implementation 46 + with versions that call the backend implementation directly. The
47 - directly, allowing the compiler to inline through the entire 47 + compiler can then inline through the entire call chain.
48 - call chain.  
49   48  
50   Non-async operations (`open`, `close`, `cancel`, `bind`, 49   Non-async operations (`open`, `close`, `cancel`, `bind`,
51   socket options) remain unchanged and dispatch through the 50   socket options) remain unchanged and dispatch through the
52   compiled library. 51   compiled library.
53   52  
54   A `native_udp_socket` IS-A `udp_socket` and can be passed to 53   A `native_udp_socket` IS-A `udp_socket` and can be passed to
55   any function expecting `udp_socket&`, in which case virtual 54   any function expecting `udp_socket&`, in which case virtual
56   dispatch is used transparently. 55   dispatch is used transparently.
57   56  
58   @tparam Backend A backend tag value (e.g., `epoll`) 57   @tparam Backend A backend tag value (e.g., `epoll`)
59   whose type provides the concrete implementation types. 58   whose type provides the concrete implementation types.
60   59  
61   @par Thread Safety 60   @par Thread Safety
62   Same as @ref udp_socket. 61   Same as @ref udp_socket.
63   62  
64   @par Example 63   @par Example
65   @par !example native_udp_socket 64   @par !example native_udp_socket
66   65  
67   @see udp_socket, epoll_t 66   @see udp_socket, epoll_t
68   */ 67   */
69   template<auto Backend> 68   template<auto Backend>
70   class native_udp_socket : public udp_socket 69   class native_udp_socket : public udp_socket
71   { 70   {
72   using backend_type = decltype(Backend); 71   using backend_type = decltype(Backend);
73   using impl_type = typename backend_type::udp_socket_type; 72   using impl_type = typename backend_type::udp_socket_type;
74   using service_type = typename backend_type::udp_service_type; 73   using service_type = typename backend_type::udp_service_type;
75   74  
HITCBC 76   28 impl_type& get_impl() noexcept 75   28 impl_type& get_impl() noexcept
77   { 76   {
HITCBC 78   28 return *static_cast<impl_type*>(h_.get()); 77   28 return *static_cast<impl_type*>(h_.get());
79   } 78   }
80   79  
81   template<class ConstBufferSequence> 80   template<class ConstBufferSequence>
82   struct native_send_to_awaitable 81   struct native_send_to_awaitable
83   : detail::bytes_op_base<native_send_to_awaitable<ConstBufferSequence>> 82   : detail::bytes_op_base<native_send_to_awaitable<ConstBufferSequence>>
84   { 83   {
85   native_udp_socket& self_; 84   native_udp_socket& self_;
86   ConstBufferSequence buffers_; 85   ConstBufferSequence buffers_;
87   endpoint dest_; 86   endpoint dest_;
88   int flags_; 87   int flags_;
89   88  
HITCBC 90   8 native_send_to_awaitable( 89   8 native_send_to_awaitable(
91   native_udp_socket& self, 90   native_udp_socket& self,
92   ConstBufferSequence buffers, 91   ConstBufferSequence buffers,
93   endpoint dest, 92   endpoint dest,
94   int flags) noexcept 93   int flags) noexcept
HITCBC 95   8 : self_(self) 94   8 : self_(self)
HITCBC 96   8 , buffers_(std::move(buffers)) 95   8 , buffers_(std::move(buffers))
HITCBC 97   8 , dest_(dest) 96   8 , dest_(dest)
HITCBC 98   8 , flags_(flags) 97   8 , flags_(flags)
99   { 98   {
HITCBC 100   8 } 99   8 }
101   100  
102   std::coroutine_handle<> 101   std::coroutine_handle<>
HITCBC 103   4 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 102   4 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
104   { 103   {
HITCBC 105   12 return self_.get_impl().send_to( 104   12 return self_.get_impl().send_to(
HITCBC 106   4 h, ex, buffers_, dest_, flags_, this->token_, &this->ec_, 105   4 h, ex, buffers_, dest_, flags_, this->token_, &this->ec_,
HITCBC 107   8 &this->bytes_); 106   8 &this->bytes_);
108   } 107   }
109   }; 108   };
110   109  
111   template<class MutableBufferSequence> 110   template<class MutableBufferSequence>
112   struct native_recv_from_awaitable 111   struct native_recv_from_awaitable
113   : detail::bytes_op_base< 112   : detail::bytes_op_base<
114   native_recv_from_awaitable<MutableBufferSequence>> 113   native_recv_from_awaitable<MutableBufferSequence>>
115   { 114   {
116   native_udp_socket& self_; 115   native_udp_socket& self_;
117   MutableBufferSequence buffers_; 116   MutableBufferSequence buffers_;
118   endpoint& source_; 117   endpoint& source_;
119   int flags_; 118   int flags_;
120   119  
HITCBC 121   12 native_recv_from_awaitable( 120   12 native_recv_from_awaitable(
122   native_udp_socket& self, 121   native_udp_socket& self,
123   MutableBufferSequence buffers, 122   MutableBufferSequence buffers,
124   endpoint& source, 123   endpoint& source,
125   int flags) noexcept 124   int flags) noexcept
HITCBC 126   12 : self_(self) 125   12 : self_(self)
HITCBC 127   12 , buffers_(std::move(buffers)) 126   12 , buffers_(std::move(buffers))
HITCBC 128   12 , source_(source) 127   12 , source_(source)
HITCBC 129   12 , flags_(flags) 128   12 , flags_(flags)
130   { 129   {
HITCBC 131   12 } 130   12 }
132   131  
133   std::coroutine_handle<> 132   std::coroutine_handle<>
HITCBC 134   8 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 133   8 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
135   { 134   {
HITCBC 136   24 return self_.get_impl().recv_from( 135   24 return self_.get_impl().recv_from(
HITCBC 137   8 h, ex, buffers_, &source_, flags_, this->token_, &this->ec_, 136   8 h, ex, buffers_, &source_, flags_, this->token_, &this->ec_,
HITCBC 138   16 &this->bytes_); 137   16 &this->bytes_);
139   } 138   }
140   }; 139   };
141   140  
142   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable> 141   struct native_wait_awaitable : detail::void_op_base<native_wait_awaitable>
143   { 142   {
144   native_udp_socket& self_; 143   native_udp_socket& self_;
145   wait_type w_; 144   wait_type w_;
146   145  
HITCBC 147   4 native_wait_awaitable(native_udp_socket& self, wait_type w) noexcept 146   4 native_wait_awaitable(native_udp_socket& self, wait_type w) noexcept
HITCBC 148   4 : self_(self) 147   4 : self_(self)
HITCBC 149   4 , w_(w) 148   4 , w_(w)
150   { 149   {
HITCBC 151   4 } 150   4 }
152   151  
153   std::coroutine_handle<> 152   std::coroutine_handle<>
HITCBC 154   2 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 153   2 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
155   { 154   {
HITCBC 156   2 return self_.get_impl().wait(h, ex, w_, this->token_, &this->ec_); 155   2 return self_.get_impl().wait(h, ex, w_, this->token_, &this->ec_);
157   } 156   }
158   }; 157   };
159   158  
160   struct native_connect_awaitable 159   struct native_connect_awaitable
161   : detail::void_op_base<native_connect_awaitable> 160   : detail::void_op_base<native_connect_awaitable>
162   { 161   {
163   native_udp_socket& self_; 162   native_udp_socket& self_;
164   endpoint endpoint_; 163   endpoint endpoint_;
165   164  
HITCBC 166   10 native_connect_awaitable(native_udp_socket& self, endpoint ep) noexcept 165   10 native_connect_awaitable(native_udp_socket& self, endpoint ep) noexcept
HITCBC 167   10 : self_(self) 166   10 : self_(self)
HITCBC 168   10 , endpoint_(ep) 167   10 , endpoint_(ep)
169   { 168   {
HITCBC 170   10 } 169   10 }
171   170  
172   std::coroutine_handle<> 171   std::coroutine_handle<>
HITCBC 173   8 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 172   8 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
174   { 173   {
HITCBC 175   24 return self_.get_impl().connect( 174   24 return self_.get_impl().connect(
HITCBC 176   24 h, ex, endpoint_, this->token_, &this->ec_); 175   24 h, ex, endpoint_, this->token_, &this->ec_);
177   } 176   }
178   }; 177   };
179   178  
180   template<class ConstBufferSequence> 179   template<class ConstBufferSequence>
181   struct native_send_awaitable 180   struct native_send_awaitable
182   : detail::bytes_op_base<native_send_awaitable<ConstBufferSequence>> 181   : detail::bytes_op_base<native_send_awaitable<ConstBufferSequence>>
183   { 182   {
184   native_udp_socket& self_; 183   native_udp_socket& self_;
185   ConstBufferSequence buffers_; 184   ConstBufferSequence buffers_;
186   int flags_; 185   int flags_;
187   186  
HITCBC 188   8 native_send_awaitable( 187   8 native_send_awaitable(
189   native_udp_socket& self, 188   native_udp_socket& self,
190   ConstBufferSequence buffers, 189   ConstBufferSequence buffers,
191   int flags) noexcept 190   int flags) noexcept
HITCBC 192   8 : self_(self) 191   8 : self_(self)
HITCBC 193   8 , buffers_(std::move(buffers)) 192   8 , buffers_(std::move(buffers))
HITCBC 194   8 , flags_(flags) 193   8 , flags_(flags)
195   { 194   {
HITCBC 196   8 } 195   8 }
197   196  
198   std::coroutine_handle<> 197   std::coroutine_handle<>
HITCBC 199   4 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 198   4 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
200   { 199   {
HITCBC 201   12 return self_.get_impl().send( 200   12 return self_.get_impl().send(
HITCBC 202   4 h, ex, buffers_, flags_, this->token_, &this->ec_, 201   4 h, ex, buffers_, flags_, this->token_, &this->ec_,
HITCBC 203   8 &this->bytes_); 202   8 &this->bytes_);
204   } 203   }
205   }; 204   };
206   205  
207   template<class MutableBufferSequence> 206   template<class MutableBufferSequence>
208   struct native_recv_awaitable 207   struct native_recv_awaitable
209   : detail::bytes_op_base<native_recv_awaitable<MutableBufferSequence>> 208   : detail::bytes_op_base<native_recv_awaitable<MutableBufferSequence>>
210   { 209   {
211   native_udp_socket& self_; 210   native_udp_socket& self_;
212   MutableBufferSequence buffers_; 211   MutableBufferSequence buffers_;
213   int flags_; 212   int flags_;
214   213  
HITCBC 215   6 native_recv_awaitable( 214   6 native_recv_awaitable(
216   native_udp_socket& self, 215   native_udp_socket& self,
217   MutableBufferSequence buffers, 216   MutableBufferSequence buffers,
218   int flags) noexcept 217   int flags) noexcept
HITCBC 219   6 : self_(self) 218   6 : self_(self)
HITCBC 220   6 , buffers_(std::move(buffers)) 219   6 , buffers_(std::move(buffers))
HITCBC 221   6 , flags_(flags) 220   6 , flags_(flags)
222   { 221   {
HITCBC 223   6 } 222   6 }
224   223  
225   std::coroutine_handle<> 224   std::coroutine_handle<>
HITCBC 226   2 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 225   2 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
227   { 226   {
HITCBC 228   6 return self_.get_impl().recv( 227   6 return self_.get_impl().recv(
HITCBC 229   2 h, ex, buffers_, flags_, this->token_, &this->ec_, 228   2 h, ex, buffers_, flags_, this->token_, &this->ec_,
HITCBC 230   4 &this->bytes_); 229   4 &this->bytes_);
231   } 230   }
232   }; 231   };
233   232  
234   public: 233   public:
235   /** Construct a native UDP socket from an execution context. 234   /** Construct a native UDP socket from an execution context.
236   235  
237 - @param ctx The execution context that will own this socket. 236 + @param ctx The execution context that owns this socket.
238   */ 237   */
HITCBC 239   42 explicit native_udp_socket(capy::execution_context& ctx) 238   42 explicit native_udp_socket(capy::execution_context& ctx)
HITCBC 240   42 : udp_socket(create_handle<service_type>(ctx)) 239   42 : udp_socket(create_handle<service_type>(ctx))
241   { 240   {
HITCBC 242   42 } 241   42 }
243   242  
244   /** Construct a native UDP socket from an executor. 243   /** Construct a native UDP socket from an executor.
245   244  
246 - @param ex The executor whose context will own the socket. 245 + @param ex The executor whose context owns the socket.
  246 +
  247 + @tparam Ex A type satisfying @ref capy::Executor. Must not
  248 + be `native_udp_socket` itself (disables implicit
  249 + conversion from move).
247   */ 250   */
248   template<class Ex> 251   template<class Ex>
249   requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) && 252   requires(!std::same_as<std::remove_cvref_t<Ex>, native_udp_socket>) &&
250   capy::Executor<Ex> 253   capy::Executor<Ex>
251   explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context()) 254   explicit native_udp_socket(Ex const& ex) : native_udp_socket(ex.context())
252   { 255   {
253   } 256   }
254   257  
255 - /// Move construct. 258 + /** Move construct.
  259 +
  260 + @param other The socket to move from.
  261 +
  262 + @pre No awaitables returned by @p other's methods exist.
  263 + @pre The execution context associated with @p other must
  264 + outlive this socket.
  265 + */
HITCBC 256   2 native_udp_socket(native_udp_socket&&) noexcept = default; 266   2 native_udp_socket(native_udp_socket&&) noexcept = default;
257   267  
258 - /// Move assign. 268 + /** Move assign.
  269 +
  270 + @param other The socket to move from.
  271 +
  272 + @pre No awaitables returned by either `*this` or @p other's
  273 + methods exist.
  274 + @pre The execution context associated with @p other must
  275 + outlive this socket.
  276 + */
259   native_udp_socket& operator=(native_udp_socket&&) noexcept = default; 277   native_udp_socket& operator=(native_udp_socket&&) noexcept = default;
260   278  
261 - native_udp_socket(native_udp_socket const&) = delete; 279 + /// Copy construction is disabled; the handle is uniquely owned.
  280 + native_udp_socket(native_udp_socket const&) = delete;
  281 + /// Copy assignment is disabled; the handle is uniquely owned.
262   native_udp_socket& operator=(native_udp_socket const&) = delete; 282   native_udp_socket& operator=(native_udp_socket const&) = delete;
263   283  
264   /** Send a datagram to the specified destination. 284   /** Send a datagram to the specified destination.
265   285  
266   Calls the backend implementation directly, bypassing virtual 286   Calls the backend implementation directly, bypassing virtual
267   dispatch. Otherwise identical to @ref udp_socket::send_to. 287   dispatch. Otherwise identical to @ref udp_socket::send_to.
268   288  
269   @param buffers The buffer sequence containing data to send. 289   @param buffers The buffer sequence containing data to send.
270   @param dest The destination endpoint. 290   @param dest The destination endpoint.
271   @param flags Message flags. 291   @param flags Message flags.
272   292  
273   @return An awaitable yielding `(error_code, std::size_t)`. 293   @return An awaitable yielding `(error_code, std::size_t)`.
274   294  
275   A closed socket reports `errc::bad_file_descriptor`. 295   A closed socket reports `errc::bad_file_descriptor`.
276   */ 296   */
277   template<capy::ConstBufferSequence CB> 297   template<capy::ConstBufferSequence CB>
278   [[nodiscard]] auto 298   [[nodiscard]] auto
HITCBC 279   8 send_to(CB const& buffers, endpoint dest, corosio::message_flags flags) 299   8 send_to(CB const& buffers, endpoint dest, corosio::message_flags flags)
280   { 300   {
HITCBC 281   8 native_send_to_awaitable<CB> aw( 301   8 native_send_to_awaitable<CB> aw(
282   *this, buffers, dest, static_cast<int>(flags)); 302   *this, buffers, dest, static_cast<int>(flags));
HITCBC 283   8 if (!is_open()) 303   8 if (!is_open())
HITCBC 284   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 304   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 285   8 return aw; 305   8 return aw;
286   } 306   }
287   307  
288   /// @overload 308   /// @overload
289   template<capy::ConstBufferSequence CB> 309   template<capy::ConstBufferSequence CB>
HITCBC 290   8 [[nodiscard]] auto send_to(CB const& buffers, endpoint dest) 310   8 [[nodiscard]] auto send_to(CB const& buffers, endpoint dest)
291   { 311   {
HITCBC 292   8 return send_to(buffers, dest, corosio::message_flags::none); 312   8 return send_to(buffers, dest, corosio::message_flags::none);
293   } 313   }
294   314  
295   /** Receive a datagram and capture the sender's endpoint. 315   /** Receive a datagram and capture the sender's endpoint.
296   316  
297   Calls the backend implementation directly, bypassing virtual 317   Calls the backend implementation directly, bypassing virtual
298   dispatch. Otherwise identical to @ref udp_socket::recv_from. 318   dispatch. Otherwise identical to @ref udp_socket::recv_from.
299   319  
300   @param buffers The buffer sequence to receive data into. 320   @param buffers The buffer sequence to receive data into.
301 - @param source Reference to an endpoint that will be set to 321 + @param source Reference to an endpoint that receives
302   the sender's address on successful completion. 322   the sender's address on successful completion.
303   @param flags Message flags (e.g. message_flags::peek). 323   @param flags Message flags (e.g. message_flags::peek).
304   324  
305   @return An awaitable yielding `(error_code, std::size_t)`. 325   @return An awaitable yielding `(error_code, std::size_t)`.
306   326  
307   A closed socket reports `errc::bad_file_descriptor`. 327   A closed socket reports `errc::bad_file_descriptor`.
308   */ 328   */
309   template<capy::MutableBufferSequence MB> 329   template<capy::MutableBufferSequence MB>
310   [[nodiscard]] auto 330   [[nodiscard]] auto
HITCBC 311   12 recv_from(MB const& buffers, endpoint& source, corosio::message_flags flags) 331   12 recv_from(MB const& buffers, endpoint& source, corosio::message_flags flags)
312   { 332   {
HITCBC 313   12 native_recv_from_awaitable<MB> aw( 333   12 native_recv_from_awaitable<MB> aw(
314   *this, buffers, source, static_cast<int>(flags)); 334   *this, buffers, source, static_cast<int>(flags));
HITCBC 315   12 if (!is_open()) 335   12 if (!is_open())
HITCBC 316   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 336   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 317   12 return aw; 337   12 return aw;
318   } 338   }
319   339  
320   /// @overload 340   /// @overload
321   template<capy::MutableBufferSequence MB> 341   template<capy::MutableBufferSequence MB>
HITCBC 322   12 [[nodiscard]] auto recv_from(MB const& buffers, endpoint& source) 342   12 [[nodiscard]] auto recv_from(MB const& buffers, endpoint& source)
323   { 343   {
HITCBC 324   12 return recv_from(buffers, source, corosio::message_flags::none); 344   12 return recv_from(buffers, source, corosio::message_flags::none);
325   } 345   }
326   346  
327   /** Asynchronously connect to set the default peer. 347   /** Asynchronously connect to set the default peer.
328   348  
329   Calls the backend implementation directly, bypassing virtual 349   Calls the backend implementation directly, bypassing virtual
330   dispatch. Otherwise identical to @ref udp_socket::connect. 350   dispatch. Otherwise identical to @ref udp_socket::connect.
331   351  
332   If the socket is not already open, it is opened automatically 352   If the socket is not already open, it is opened automatically
333   using the address family of @p ep. 353   using the address family of @p ep.
334   354  
335   @param ep The remote endpoint to connect to. 355   @param ep The remote endpoint to connect to.
336   356  
337   @return An awaitable yielding `io_result<>`. 357   @return An awaitable yielding `io_result<>`.
338   358  
339   If the socket needs to be opened and the open fails, the 359   If the socket needs to be opened and the open fails, the
340   awaitable completes immediately with that error. 360   awaitable completes immediately with that error.
341   */ 361   */
HITCBC 342   10 [[nodiscard]] auto connect(endpoint ep) 362   10 [[nodiscard]] auto connect(endpoint ep)
343   { 363   {
HITCBC 344   10 native_connect_awaitable aw(*this, ep); 364   10 native_connect_awaitable aw(*this, ep);
HITCBC 345   10 if (!is_open()) 365   10 if (!is_open())
HITCBC 346   4 aw.ec_ = open(ep.address().family()); 366   4 aw.ec_ = open(ep.address().family());
HITCBC 347   10 return aw; 367   10 return aw;
348   } 368   }
349   369  
350   /** Send a datagram to the connected peer. 370   /** Send a datagram to the connected peer.
351   371  
352   Calls the backend implementation directly, bypassing virtual 372   Calls the backend implementation directly, bypassing virtual
353   dispatch. Otherwise identical to @ref udp_socket::send. 373   dispatch. Otherwise identical to @ref udp_socket::send.
354   374  
355   @param buffers The buffer sequence containing data to send. 375   @param buffers The buffer sequence containing data to send.
356   @param flags Message flags. 376   @param flags Message flags.
357   377  
358   @return An awaitable yielding `(error_code, std::size_t)`. 378   @return An awaitable yielding `(error_code, std::size_t)`.
359   379  
360   A closed socket reports `errc::bad_file_descriptor`. 380   A closed socket reports `errc::bad_file_descriptor`.
361   */ 381   */
362   template<capy::ConstBufferSequence CB> 382   template<capy::ConstBufferSequence CB>
HITCBC 363   8 [[nodiscard]] auto send(CB const& buffers, corosio::message_flags flags) 383   8 [[nodiscard]] auto send(CB const& buffers, corosio::message_flags flags)
364   { 384   {
HITCBC 365   8 native_send_awaitable<CB> aw(*this, buffers, static_cast<int>(flags)); 385   8 native_send_awaitable<CB> aw(*this, buffers, static_cast<int>(flags));
HITCBC 366   8 if (!is_open()) 386   8 if (!is_open())
HITCBC 367   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 387   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 368   8 return aw; 388   8 return aw;
369   } 389   }
370   390  
371   /// @overload 391   /// @overload
372   template<capy::ConstBufferSequence CB> 392   template<capy::ConstBufferSequence CB>
HITCBC 373   8 [[nodiscard]] auto send(CB const& buffers) 393   8 [[nodiscard]] auto send(CB const& buffers)
374   { 394   {
HITCBC 375   8 return send(buffers, corosio::message_flags::none); 395   8 return send(buffers, corosio::message_flags::none);
376   } 396   }
377   397  
378   /** Receive a datagram from the connected peer. 398   /** Receive a datagram from the connected peer.
379   399  
380   Calls the backend implementation directly, bypassing virtual 400   Calls the backend implementation directly, bypassing virtual
381   dispatch. Otherwise identical to @ref udp_socket::recv. 401   dispatch. Otherwise identical to @ref udp_socket::recv.
382   402  
383   @param buffers The buffer sequence to receive data into. 403   @param buffers The buffer sequence to receive data into.
384   @param flags Message flags (e.g. message_flags::peek). 404   @param flags Message flags (e.g. message_flags::peek).
385   405  
386   @return An awaitable yielding `(error_code, std::size_t)`. 406   @return An awaitable yielding `(error_code, std::size_t)`.
387   407  
388   A closed socket reports `errc::bad_file_descriptor`. 408   A closed socket reports `errc::bad_file_descriptor`.
389   */ 409   */
390   template<capy::MutableBufferSequence MB> 410   template<capy::MutableBufferSequence MB>
HITCBC 391   6 [[nodiscard]] auto recv(MB const& buffers, corosio::message_flags flags) 411   6 [[nodiscard]] auto recv(MB const& buffers, corosio::message_flags flags)
392   { 412   {
HITCBC 393   6 native_recv_awaitable<MB> aw(*this, buffers, static_cast<int>(flags)); 413   6 native_recv_awaitable<MB> aw(*this, buffers, static_cast<int>(flags));
HITCBC 394   6 if (!is_open()) 414   6 if (!is_open())
HITCBC 395   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 415   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 396   6 return aw; 416   6 return aw;
397   } 417   }
398   418  
399   /// @overload 419   /// @overload
400   template<capy::MutableBufferSequence MB> 420   template<capy::MutableBufferSequence MB>
HITCBC 401   6 [[nodiscard]] auto recv(MB const& buffers) 421   6 [[nodiscard]] auto recv(MB const& buffers)
402   { 422   {
HITCBC 403   6 return recv(buffers, corosio::message_flags::none); 423   6 return recv(buffers, corosio::message_flags::none);
404   } 424   }
405   425  
406   /** Asynchronously wait for the socket to be ready. 426   /** Asynchronously wait for the socket to be ready.
407   427  
408   Calls the backend implementation directly, bypassing virtual 428   Calls the backend implementation directly, bypassing virtual
409   dispatch. Otherwise identical to @ref udp_socket::wait. 429   dispatch. Otherwise identical to @ref udp_socket::wait.
410   430  
411   @param w The wait direction (read, write, or error). 431   @param w The wait direction (read, write, or error).
412   432  
413   @return An awaitable yielding `io_result<>`. 433   @return An awaitable yielding `io_result<>`.
414   */ 434   */
HITCBC 415   4 [[nodiscard]] auto wait(wait_type w) 435   4 [[nodiscard]] auto wait(wait_type w)
416   { 436   {
HITCBC 417   4 return native_wait_awaitable(*this, w); 437   4 return native_wait_awaitable(*this, w);
418   } 438   }
419   }; 439   };
420   440  
421   } // namespace boost::corosio 441   } // namespace boost::corosio
422   442  
423   #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP 443   #endif // BOOST_COROSIO_NATIVE_NATIVE_UDP_SOCKET_HPP