95.95% Lines (71/74) 100.00% Functions (36/36)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_SOCKET_OPTION_HPP 10   #ifndef BOOST_COROSIO_SOCKET_OPTION_HPP
11   #define BOOST_COROSIO_SOCKET_OPTION_HPP 11   #define BOOST_COROSIO_SOCKET_OPTION_HPP
12   12  
13   #include <boost/corosio/detail/config.hpp> 13   #include <boost/corosio/detail/config.hpp>
14   #include <boost/corosio/ipv4_address.hpp> 14   #include <boost/corosio/ipv4_address.hpp>
15   #include <boost/corosio/ipv6_address.hpp> 15   #include <boost/corosio/ipv6_address.hpp>
16   16  
17   #include <cstddef> 17   #include <cstddef>
18   18  
19   /** @file socket_option.hpp 19   /** @file socket_option.hpp
20   20  
21   Type-erased socket option types that avoid platform-specific 21   Type-erased socket option types that avoid platform-specific
22   headers. The protocol level and option name for each type are 22   headers. The protocol level and option name for each type are
23   resolved at link time via the compiled library. 23   resolved at link time via the compiled library.
24   24  
25   For an inline (zero-overhead) alternative that includes platform 25   For an inline (zero-overhead) alternative that includes platform
26   headers, use `<boost/corosio/native/native_socket_option.hpp>` 26   headers, use `<boost/corosio/native/native_socket_option.hpp>`
27   (`boost::corosio::native_socket_option`). 27   (`boost::corosio::native_socket_option`).
28   28  
29   Both variants satisfy the same option-type interface and work 29   Both variants satisfy the same option-type interface and work
30   interchangeably with `tcp_socket::set_option` / 30   interchangeably with `tcp_socket::set_option` /
31   `tcp_socket::get_option` and the corresponding acceptor methods. 31   `tcp_socket::get_option` and the corresponding acceptor methods.
32   32  
33   @see native_socket_option 33   @see native_socket_option
34   */ 34   */
35   35  
36   namespace boost::corosio::socket_option { 36   namespace boost::corosio::socket_option {
37   37  
38   /** Base class for concrete boolean socket options. 38   /** Base class for concrete boolean socket options.
39   39  
40   Stores a boolean as an `int` suitable for `setsockopt`/`getsockopt`. 40   Stores a boolean as an `int` suitable for `setsockopt`/`getsockopt`.
41   Derived types provide `level()` and `name()` for the specific option. 41   Derived types provide `level()` and `name()` for the specific option.
42   */ 42   */
43   class BOOST_COROSIO_DECL boolean_option 43   class BOOST_COROSIO_DECL boolean_option
44   { 44   {
45   int value_ = 0; 45   int value_ = 0;
46   46  
47   public: 47   public:
48   /// Construct with default value (disabled). 48   /// Construct with default value (disabled).
49   boolean_option() = default; 49   boolean_option() = default;
50   50  
51   /** Construct with an explicit value. 51   /** Construct with an explicit value.
52   52  
53   @param v `true` to enable the option, `false` to disable. 53   @param v `true` to enable the option, `false` to disable.
54   */ 54   */
HITCBC 55   455 explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {} 55   469 explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {}
56   56  
57   /// Assign a new value. 57   /// Assign a new value.
HITCBC 58   4 boolean_option& operator=(bool v) noexcept 58   4 boolean_option& operator=(bool v) noexcept
59   { 59   {
HITCBC 60   4 value_ = v ? 1 : 0; 60   4 value_ = v ? 1 : 0;
HITCBC 61   4 return *this; 61   4 return *this;
62   } 62   }
63   63  
64   /// Return the option value. 64   /// Return the option value.
HITCBC 65   62 bool value() const noexcept 65   62 bool value() const noexcept
66   { 66   {
HITCBC 67   62 return value_ != 0; 67   62 return value_ != 0;
68   } 68   }
69   69  
70   /// Return the option value. 70   /// Return the option value.
HITCBC 71   4 explicit operator bool() const noexcept 71   4 explicit operator bool() const noexcept
72   { 72   {
HITCBC 73   4 return value_ != 0; 73   4 return value_ != 0;
74   } 74   }
75   75  
76   /// Return the negated option value. 76   /// Return the negated option value.
HITCBC 77   4 bool operator!() const noexcept 77   4 bool operator!() const noexcept
78   { 78   {
HITCBC 79   4 return value_ == 0; 79   4 return value_ == 0;
80   } 80   }
81   81  
82   /// Return a pointer to the underlying storage. 82   /// Return a pointer to the underlying storage.
HITCBC 83   66 void* data() noexcept 83   78 void* data() noexcept
84   { 84   {
HITCBC 85   66 return &value_; 85   78 return &value_;
86   } 86   }
87   87  
88   /// Return a pointer to the underlying storage. 88   /// Return a pointer to the underlying storage.
HITCBC 89   449 void const* data() const noexcept 89   463 void const* data() const noexcept
90   { 90   {
HITCBC 91   449 return &value_; 91   463 return &value_;
92   } 92   }
93   93  
94   /// Return the size of the underlying storage. 94   /// Return the size of the underlying storage.
HITCBC 95   515 std::size_t size() const noexcept 95   541 std::size_t size() const noexcept
96   { 96   {
HITCBC 97   515 return sizeof(value_); 97   541 return sizeof(value_);
98   } 98   }
99   99  
100   /** Normalize after `getsockopt` returns fewer bytes than expected. 100   /** Normalize after `getsockopt` returns fewer bytes than expected.
101   101  
102   Windows Vista+ may write only 1 byte for boolean options. 102   Windows Vista+ may write only 1 byte for boolean options.
103   103  
104   @param s The number of bytes actually written by `getsockopt`. 104   @param s The number of bytes actually written by `getsockopt`.
105   */ 105   */
HITCBC 106   66 void resize(std::size_t s) noexcept 106   66 void resize(std::size_t s) noexcept
107   { 107   {
HITCBC 108   66 if (s == sizeof(char)) 108   66 if (s == sizeof(char))
MISUBC 109   value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0; 109   value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
HITCBC 110   66 } 110   66 }
111   }; 111   };
112   112  
113   /** Base class for concrete integer socket options. 113   /** Base class for concrete integer socket options.
114   114  
115   Stores an integer suitable for `setsockopt`/`getsockopt`. 115   Stores an integer suitable for `setsockopt`/`getsockopt`.
116   Derived types provide `level()` and `name()` for the specific option. 116   Derived types provide `level()` and `name()` for the specific option.
117   */ 117   */
118   class BOOST_COROSIO_DECL integer_option 118   class BOOST_COROSIO_DECL integer_option
119   { 119   {
120   int value_ = 0; 120   int value_ = 0;
121   121  
122   public: 122   public:
123   /// Construct with default value (zero). 123   /// Construct with default value (zero).
124   integer_option() = default; 124   integer_option() = default;
125   125  
126   /** Construct with an explicit value. 126   /** Construct with an explicit value.
127   127  
128   @param v The option value. 128   @param v The option value.
129   */ 129   */
HITCBC 130   63 explicit integer_option(int v) noexcept : value_(v) {} 130   65 explicit integer_option(int v) noexcept : value_(v) {}
131   131  
132   /// Assign a new value. 132   /// Assign a new value.
HITCBC 133   2 integer_option& operator=(int v) noexcept 133   2 integer_option& operator=(int v) noexcept
134   { 134   {
HITCBC 135   2 value_ = v; 135   2 value_ = v;
HITCBC 136   2 return *this; 136   2 return *this;
137   } 137   }
138   138  
139   /// Return the option value. 139   /// Return the option value.
HITCBC 140   46 int value() const noexcept 140   46 int value() const noexcept
141   { 141   {
HITCBC 142   46 return value_; 142   46 return value_;
143   } 143   }
144   144  
145   /// Return a pointer to the underlying storage. 145   /// Return a pointer to the underlying storage.
HITCBC 146   44 void* data() noexcept 146   44 void* data() noexcept
147   { 147   {
HITCBC 148   44 return &value_; 148   44 return &value_;
149   } 149   }
150   150  
151   /// Return a pointer to the underlying storage. 151   /// Return a pointer to the underlying storage.
HITCBC 152   61 void const* data() const noexcept 152   61 void const* data() const noexcept
153   { 153   {
HITCBC 154   61 return &value_; 154   61 return &value_;
155   } 155   }
156   156  
157   /// Return the size of the underlying storage. 157   /// Return the size of the underlying storage.
HITCBC 158   105 std::size_t size() const noexcept 158   105 std::size_t size() const noexcept
159   { 159   {
HITCBC 160   105 return sizeof(value_); 160   105 return sizeof(value_);
161   } 161   }
162   162  
163   /** Normalize after `getsockopt` returns fewer bytes than expected. 163   /** Normalize after `getsockopt` returns fewer bytes than expected.
164   164  
165   @param s The number of bytes actually written by `getsockopt`. 165   @param s The number of bytes actually written by `getsockopt`.
166   */ 166   */
HITCBC 167   44 void resize(std::size_t s) noexcept 167   44 void resize(std::size_t s) noexcept
168   { 168   {
HITCBC 169   44 if (s == sizeof(char)) 169   44 if (s == sizeof(char))
MISUBC 170   value_ = 170   value_ =
MISUBC 171   static_cast<int>(*reinterpret_cast<unsigned char*>(&value_)); 171   static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
HITCBC 172   44 } 172   44 }
173   }; 173   };
174   174  
175   /** Base class for concrete boolean socket options with single-byte storage. 175   /** Base class for concrete boolean socket options with single-byte storage.
176   176  
177   Some BSD-derived kernels (macOS, FreeBSD) require certain IPv4 multicast 177   Some BSD-derived kernels (macOS, FreeBSD) require certain IPv4 multicast
178   options (`IP_MULTICAST_LOOP`) to be set with a one-byte value and return 178   options (`IP_MULTICAST_LOOP`) to be set with a one-byte value and return
179   `EINVAL` for the four-byte form that Linux accepts. This base provides 179   `EINVAL` for the four-byte form that Linux accepts. This base provides
180   `unsigned char` storage so the same options work on every platform. 180   `unsigned char` storage so the same options work on every platform.
181   */ 181   */
182   class BOOST_COROSIO_DECL byte_boolean_option 182   class BOOST_COROSIO_DECL byte_boolean_option
183   { 183   {
184   unsigned char value_ = 0; 184   unsigned char value_ = 0;
185   185  
186   public: 186   public:
187   /// Construct with default value (disabled). 187   /// Construct with default value (disabled).
188   byte_boolean_option() = default; 188   byte_boolean_option() = default;
189   189  
190   /** Construct with an explicit value. 190   /** Construct with an explicit value.
191   191  
192   @param v `true` to enable the option, `false` to disable. 192   @param v `true` to enable the option, `false` to disable.
193   */ 193   */
HITCBC 194   10 explicit byte_boolean_option(bool v) noexcept : value_(v ? 1 : 0) {} 194   10 explicit byte_boolean_option(bool v) noexcept : value_(v ? 1 : 0) {}
195   195  
196   /// Assign a new value. 196   /// Assign a new value.
197   byte_boolean_option& operator=(bool v) noexcept 197   byte_boolean_option& operator=(bool v) noexcept
198   { 198   {
199   value_ = v ? 1 : 0; 199   value_ = v ? 1 : 0;
200   return *this; 200   return *this;
201   } 201   }
202   202  
203   /// Return the option value. 203   /// Return the option value.
HITCBC 204   8 bool value() const noexcept 204   8 bool value() const noexcept
205   { 205   {
HITCBC 206   8 return value_ != 0; 206   8 return value_ != 0;
207   } 207   }
208   208  
209   /// Return the option value. 209   /// Return the option value.
210   explicit operator bool() const noexcept 210   explicit operator bool() const noexcept
211   { 211   {
212   return value_ != 0; 212   return value_ != 0;
213   } 213   }
214   214  
215   /// Return the negated option value. 215   /// Return the negated option value.
216   bool operator!() const noexcept 216   bool operator!() const noexcept
217   { 217   {
218   return value_ == 0; 218   return value_ == 0;
219   } 219   }
220   220  
221   /// Return a pointer to the underlying storage. 221   /// Return a pointer to the underlying storage.
HITCBC 222   8 void* data() noexcept 222   8 void* data() noexcept
223   { 223   {
HITCBC 224   8 return &value_; 224   8 return &value_;
225   } 225   }
226   226  
227   /// Return a pointer to the underlying storage. 227   /// Return a pointer to the underlying storage.
HITCBC 228   10 void const* data() const noexcept 228   10 void const* data() const noexcept
229   { 229   {
HITCBC 230   10 return &value_; 230   10 return &value_;
231   } 231   }
232   232  
233   /// Return the size of the underlying storage. 233   /// Return the size of the underlying storage.
HITCBC 234   18 std::size_t size() const noexcept 234   18 std::size_t size() const noexcept
235   { 235   {
HITCBC 236   18 return sizeof(value_); 236   18 return sizeof(value_);
237   } 237   }
238   238  
239   /// Storage is already one byte; no normalization needed. 239   /// Storage is already one byte; no normalization needed.
HITCBC 240   8 void resize(std::size_t) noexcept {} 240   8 void resize(std::size_t) noexcept {}
241   }; 241   };
242   242  
243   /** Base class for concrete integer socket options with single-byte storage. 243   /** Base class for concrete integer socket options with single-byte storage.
244   244  
245   Same rationale as `byte_boolean_option`: BSD-derived kernels require 245   Same rationale as `byte_boolean_option`: BSD-derived kernels require
246   `IP_MULTICAST_TTL` to be set with a one-byte value. Linux accepts 246   `IP_MULTICAST_TTL` to be set with a one-byte value. Linux accepts
247   one-byte too, so single-byte storage is portable. 247   one-byte too, so single-byte storage is portable.
248   */ 248   */
249   class BOOST_COROSIO_DECL byte_integer_option 249   class BOOST_COROSIO_DECL byte_integer_option
250   { 250   {
251   unsigned char value_ = 0; 251   unsigned char value_ = 0;
252   252  
253   public: 253   public:
254   /// Construct with default value (zero). 254   /// Construct with default value (zero).
255   byte_integer_option() = default; 255   byte_integer_option() = default;
256   256  
257   /** Construct with an explicit value. 257   /** Construct with an explicit value.
258   258  
259   @param v The option value; truncated to one byte. 259   @param v The option value; truncated to one byte.
260   */ 260   */
HITCBC 261   4 explicit byte_integer_option(int v) noexcept 261   4 explicit byte_integer_option(int v) noexcept
HITCBC 262   4 : value_(static_cast<unsigned char>(v)) 262   4 : value_(static_cast<unsigned char>(v))
HITCBC 263   4 {} 263   4 {}
264   264  
265   /// Assign a new value; truncated to one byte. 265   /// Assign a new value; truncated to one byte.
266   byte_integer_option& operator=(int v) noexcept 266   byte_integer_option& operator=(int v) noexcept
267   { 267   {
268   value_ = static_cast<unsigned char>(v); 268   value_ = static_cast<unsigned char>(v);
269   return *this; 269   return *this;
270   } 270   }
271   271  
272   /// Return the option value. 272   /// Return the option value.
HITCBC 273   4 int value() const noexcept 273   4 int value() const noexcept
274   { 274   {
HITCBC 275   4 return value_; 275   4 return value_;
276   } 276   }
277   277  
278   /// Return a pointer to the underlying storage. 278   /// Return a pointer to the underlying storage.
HITCBC 279   4 void* data() noexcept 279   4 void* data() noexcept
280   { 280   {
HITCBC 281   4 return &value_; 281   4 return &value_;
282   } 282   }
283   283  
284   /// Return a pointer to the underlying storage. 284   /// Return a pointer to the underlying storage.
HITCBC 285   4 void const* data() const noexcept 285   4 void const* data() const noexcept
286   { 286   {
HITCBC 287   4 return &value_; 287   4 return &value_;
288   } 288   }
289   289  
290   /// Return the size of the underlying storage. 290   /// Return the size of the underlying storage.
HITCBC 291   8 std::size_t size() const noexcept 291   8 std::size_t size() const noexcept
292   { 292   {
HITCBC 293   8 return sizeof(value_); 293   8 return sizeof(value_);
294   } 294   }
295   295  
296   /// Storage is already one byte; no normalization needed. 296   /// Storage is already one byte; no normalization needed.
HITCBC 297   4 void resize(std::size_t) noexcept {} 297   4 void resize(std::size_t) noexcept {}
298   }; 298   };
299   299  
300   /** Disable Nagle's algorithm (TCP_NODELAY). 300   /** Disable Nagle's algorithm (TCP_NODELAY).
301   301  
302   @par Example 302   @par Example
303   @code 303   @code
304   sock.set_option( socket_option::no_delay( true ) ); 304   sock.set_option( socket_option::no_delay( true ) );
305   auto nd = sock.get_option<socket_option::no_delay>(); 305   auto nd = sock.get_option<socket_option::no_delay>();
306 - if ( nd.value() ) 306 + bool disabled = nd.value(); // true: Nagle's algorithm is off
307 - // Nagle's algorithm is disabled  
308   @endcode 307   @endcode
309   */ 308   */
310   class BOOST_COROSIO_DECL no_delay : public boolean_option 309   class BOOST_COROSIO_DECL no_delay : public boolean_option
311   { 310   {
312   public: 311   public:
313   using boolean_option::boolean_option; 312   using boolean_option::boolean_option;
314   using boolean_option::operator=; 313   using boolean_option::operator=;
315   314  
316   /// Return the protocol level. 315   /// Return the protocol level.
317   static int level() noexcept; 316   static int level() noexcept;
318   317  
319   /// Return the option name. 318   /// Return the option name.
320   static int name() noexcept; 319   static int name() noexcept;
321   }; 320   };
322   321  
323   /** Enable periodic keepalive probes (SO_KEEPALIVE). 322   /** Enable periodic keepalive probes (SO_KEEPALIVE).
324   323  
325   @par Example 324   @par Example
326   @code 325   @code
327   sock.set_option( socket_option::keep_alive( true ) ); 326   sock.set_option( socket_option::keep_alive( true ) );
328   @endcode 327   @endcode
329   */ 328   */
330   class BOOST_COROSIO_DECL keep_alive : public boolean_option 329   class BOOST_COROSIO_DECL keep_alive : public boolean_option
331   { 330   {
332   public: 331   public:
333   using boolean_option::boolean_option; 332   using boolean_option::boolean_option;
334   using boolean_option::operator=; 333   using boolean_option::operator=;
335   334  
336   /// Return the protocol level. 335   /// Return the protocol level.
337   static int level() noexcept; 336   static int level() noexcept;
338   337  
339   /// Return the option name. 338   /// Return the option name.
340   static int name() noexcept; 339   static int name() noexcept;
341   }; 340   };
342   341  
343   /** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY). 342   /** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
344   343  
345   When enabled, the socket only accepts IPv6 connections. 344   When enabled, the socket only accepts IPv6 connections.
346   When disabled, the socket accepts both IPv4 and IPv6 345   When disabled, the socket accepts both IPv4 and IPv6
347   connections (dual-stack mode). 346   connections (dual-stack mode).
348   347  
349   @par Example 348   @par Example
350   @code 349   @code
351   sock.set_option( socket_option::v6_only( true ) ); 350   sock.set_option( socket_option::v6_only( true ) );
352   @endcode 351   @endcode
353   */ 352   */
354   class BOOST_COROSIO_DECL v6_only : public boolean_option 353   class BOOST_COROSIO_DECL v6_only : public boolean_option
355   { 354   {
356   public: 355   public:
357   using boolean_option::boolean_option; 356   using boolean_option::boolean_option;
358   using boolean_option::operator=; 357   using boolean_option::operator=;
359   358  
360   /// Return the protocol level. 359   /// Return the protocol level.
361   static int level() noexcept; 360   static int level() noexcept;
362   361  
363   /// Return the option name. 362   /// Return the option name.
364   static int name() noexcept; 363   static int name() noexcept;
365   }; 364   };
366   365  
367   /** Allow local address reuse (SO_REUSEADDR). 366   /** Allow local address reuse (SO_REUSEADDR).
368   367  
369   @par Example 368   @par Example
370   @code 369   @code
371   acc.set_option( socket_option::reuse_address( true ) ); 370   acc.set_option( socket_option::reuse_address( true ) );
372   @endcode 371   @endcode
373   */ 372   */
374   class BOOST_COROSIO_DECL reuse_address : public boolean_option 373   class BOOST_COROSIO_DECL reuse_address : public boolean_option
375   { 374   {
376   public: 375   public:
377   using boolean_option::boolean_option; 376   using boolean_option::boolean_option;
378   using boolean_option::operator=; 377   using boolean_option::operator=;
379   378  
380   /// Return the protocol level. 379   /// Return the protocol level.
381   static int level() noexcept; 380   static int level() noexcept;
382   381  
383   /// Return the option name. 382   /// Return the option name.
384   static int name() noexcept; 383   static int name() noexcept;
385   }; 384   };
386   385  
387   /** Allow sending to broadcast addresses (SO_BROADCAST). 386   /** Allow sending to broadcast addresses (SO_BROADCAST).
388   387  
389   Required for UDP sockets that send to broadcast addresses 388   Required for UDP sockets that send to broadcast addresses
390   such as 255.255.255.255. Without this option, `send_to` 389   such as 255.255.255.255. Without this option, `send_to`
391   returns an error. 390   returns an error.
392   391  
393   @par Example 392   @par Example
394   @code 393   @code
395   udp_socket sock( ioc ); 394   udp_socket sock( ioc );
396 - sock.open(); 395 + if ( auto ec = sock.open() )
  396 + return;
397   sock.set_option( socket_option::broadcast( true ) ); 397   sock.set_option( socket_option::broadcast( true ) );
398   @endcode 398   @endcode
399   */ 399   */
400   class BOOST_COROSIO_DECL broadcast : public boolean_option 400   class BOOST_COROSIO_DECL broadcast : public boolean_option
401   { 401   {
402   public: 402   public:
403   using boolean_option::boolean_option; 403   using boolean_option::boolean_option;
404   using boolean_option::operator=; 404   using boolean_option::operator=;
405   405  
406   /// Return the protocol level. 406   /// Return the protocol level.
407   static int level() noexcept; 407   static int level() noexcept;
408   408  
409   /// Return the option name. 409   /// Return the option name.
410   static int name() noexcept; 410   static int name() noexcept;
411   }; 411   };
412   412  
413   /** Allow multiple sockets to bind to the same port (SO_REUSEPORT). 413   /** Allow multiple sockets to bind to the same port (SO_REUSEPORT).
414   414  
415   Not available on all platforms. On unsupported platforms, 415   Not available on all platforms. On unsupported platforms,
416 - `set_option` will return an error. 416 + `set_option` throws `std::system_error`.
417   417  
418   @par Example 418   @par Example
419   @code 419   @code
420 - acc.open( tcp::v6() ); 420 + if ( auto ec = acc.open( tcp::v6() ) )
  421 + return;
421   acc.set_option( socket_option::reuse_port( true ) ); 422   acc.set_option( socket_option::reuse_port( true ) );
422 - acc.bind( endpoint( ipv6_address::any(), 8080 ) ); 423 + if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
423 - acc.listen(); 424 + return;
  425 + if ( auto ec = acc.listen() )
  426 + return;
424   @endcode 427   @endcode
425   */ 428   */
426   class BOOST_COROSIO_DECL reuse_port : public boolean_option 429   class BOOST_COROSIO_DECL reuse_port : public boolean_option
427   { 430   {
428   public: 431   public:
429   using boolean_option::boolean_option; 432   using boolean_option::boolean_option;
430   using boolean_option::operator=; 433   using boolean_option::operator=;
431   434  
432   /// Return the protocol level. 435   /// Return the protocol level.
433   static int level() noexcept; 436   static int level() noexcept;
434   437  
435   /// Return the option name. 438   /// Return the option name.
436   static int name() noexcept; 439   static int name() noexcept;
437   }; 440   };
438   441  
439   /** Set the receive buffer size (SO_RCVBUF). 442   /** Set the receive buffer size (SO_RCVBUF).
440   443  
441   @par Example 444   @par Example
442   @code 445   @code
443   sock.set_option( socket_option::receive_buffer_size( 65536 ) ); 446   sock.set_option( socket_option::receive_buffer_size( 65536 ) );
444   auto opt = sock.get_option<socket_option::receive_buffer_size>(); 447   auto opt = sock.get_option<socket_option::receive_buffer_size>();
445   int sz = opt.value(); 448   int sz = opt.value();
446   @endcode 449   @endcode
447   */ 450   */
448   class BOOST_COROSIO_DECL receive_buffer_size : public integer_option 451   class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
449   { 452   {
450   public: 453   public:
451   using integer_option::integer_option; 454   using integer_option::integer_option;
452   using integer_option::operator=; 455   using integer_option::operator=;
453   456  
454   /// Return the protocol level. 457   /// Return the protocol level.
455   static int level() noexcept; 458   static int level() noexcept;
456   459  
457   /// Return the option name. 460   /// Return the option name.
458   static int name() noexcept; 461   static int name() noexcept;
459   }; 462   };
460   463  
461   /** Set the send buffer size (SO_SNDBUF). 464   /** Set the send buffer size (SO_SNDBUF).
462   465  
463   @par Example 466   @par Example
464   @code 467   @code
465   sock.set_option( socket_option::send_buffer_size( 65536 ) ); 468   sock.set_option( socket_option::send_buffer_size( 65536 ) );
466   @endcode 469   @endcode
467   */ 470   */
468   class BOOST_COROSIO_DECL send_buffer_size : public integer_option 471   class BOOST_COROSIO_DECL send_buffer_size : public integer_option
469   { 472   {
470   public: 473   public:
471   using integer_option::integer_option; 474   using integer_option::integer_option;
472   using integer_option::operator=; 475   using integer_option::operator=;
473   476  
474   /// Return the protocol level. 477   /// Return the protocol level.
475   static int level() noexcept; 478   static int level() noexcept;
476   479  
477   /// Return the option name. 480   /// Return the option name.
478   static int name() noexcept; 481   static int name() noexcept;
479   }; 482   };
480   483  
481   /** The SO_LINGER socket option. 484   /** The SO_LINGER socket option.
482   485  
483   Controls behavior when closing a socket with unsent data. 486   Controls behavior when closing a socket with unsent data.
484   When enabled, `close()` blocks until pending data is sent 487   When enabled, `close()` blocks until pending data is sent
485   or the timeout expires. 488   or the timeout expires.
486   489  
487   @par Example 490   @par Example
488   @code 491   @code
489   sock.set_option( socket_option::linger( true, 5 ) ); 492   sock.set_option( socket_option::linger( true, 5 ) );
490   auto opt = sock.get_option<socket_option::linger>(); 493   auto opt = sock.get_option<socket_option::linger>();
491   if ( opt.enabled() ) 494   if ( opt.enabled() )
492   std::cout << "linger timeout: " << opt.timeout() << "s\n"; 495   std::cout << "linger timeout: " << opt.timeout() << "s\n";
493   @endcode 496   @endcode
494   */ 497   */
495   class BOOST_COROSIO_DECL linger 498   class BOOST_COROSIO_DECL linger
496   { 499   {
497   // Opaque storage for the platform's struct linger. 500   // Opaque storage for the platform's struct linger.
498   // POSIX: { int, int } = 8 bytes. 501   // POSIX: { int, int } = 8 bytes.
499   // Windows: { u_short, u_short } = 4 bytes. 502   // Windows: { u_short, u_short } = 4 bytes.
500   static constexpr std::size_t max_storage_ = 8; 503   static constexpr std::size_t max_storage_ = 8;
501   alignas(4) unsigned char storage_[max_storage_]{}; 504   alignas(4) unsigned char storage_[max_storage_]{};
502   505  
503   public: 506   public:
504   /// Construct with default values (disabled, zero timeout). 507   /// Construct with default values (disabled, zero timeout).
505   linger() noexcept = default; 508   linger() noexcept = default;
506   509  
507   /** Construct with explicit values. 510   /** Construct with explicit values.
508   511  
509   @param enabled `true` to enable linger behavior on close. 512   @param enabled `true` to enable linger behavior on close.
510   @param timeout The linger timeout in seconds. 513   @param timeout The linger timeout in seconds.
511   */ 514   */
512   linger(bool enabled, int timeout) noexcept; 515   linger(bool enabled, int timeout) noexcept;
513   516  
514   /// Return whether linger is enabled. 517   /// Return whether linger is enabled.
515   bool enabled() const noexcept; 518   bool enabled() const noexcept;
516   519  
517   /// Set whether linger is enabled. 520   /// Set whether linger is enabled.
518   void enabled(bool v) noexcept; 521   void enabled(bool v) noexcept;
519   522  
520   /// Return the linger timeout in seconds. 523   /// Return the linger timeout in seconds.
521   int timeout() const noexcept; 524   int timeout() const noexcept;
522   525  
523   /// Set the linger timeout in seconds. 526   /// Set the linger timeout in seconds.
524   void timeout(int v) noexcept; 527   void timeout(int v) noexcept;
525   528  
526   /// Return the protocol level. 529   /// Return the protocol level.
527   static int level() noexcept; 530   static int level() noexcept;
528   531  
529   /// Return the option name. 532   /// Return the option name.
530   static int name() noexcept; 533   static int name() noexcept;
531   534  
532   /// Return a pointer to the underlying storage. 535   /// Return a pointer to the underlying storage.
HITCBC 533   12 void* data() noexcept 536   12 void* data() noexcept
534   { 537   {
HITCBC 535   12 return storage_; 538   12 return storage_;
536   } 539   }
537   540  
538   /// Return a pointer to the underlying storage. 541   /// Return a pointer to the underlying storage.
HITCBC 539   152 void const* data() const noexcept 542   152 void const* data() const noexcept
540   { 543   {
HITCBC 541   152 return storage_; 544   152 return storage_;
542   } 545   }
543   546  
544   /// Return the size of the underlying storage. 547   /// Return the size of the underlying storage.
545   std::size_t size() const noexcept; 548   std::size_t size() const noexcept;
546   549  
547   /** Normalize after `getsockopt`. 550   /** Normalize after `getsockopt`.
548   551  
549   No-op — `struct linger` is always returned at full size. 552   No-op — `struct linger` is always returned at full size.
550   553  
551   @param s The number of bytes actually written by `getsockopt`. 554   @param s The number of bytes actually written by `getsockopt`.
552   */ 555   */
HITCBC 553   12 void resize(std::size_t) noexcept {} 556   12 void resize(std::size_t) noexcept {}
554   }; 557   };
555   558  
556   /** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP). 559   /** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
557   560  
558   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD) 561   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD)
559   reject the four-byte form with `EINVAL`. Linux accepts either size. 562   reject the four-byte form with `EINVAL`. Linux accepts either size.
560   563  
561   @par Example 564   @par Example
562   @code 565   @code
563   sock.set_option( socket_option::multicast_loop_v4( true ) ); 566   sock.set_option( socket_option::multicast_loop_v4( true ) );
564   @endcode 567   @endcode
565   */ 568   */
566   class BOOST_COROSIO_DECL multicast_loop_v4 : public byte_boolean_option 569   class BOOST_COROSIO_DECL multicast_loop_v4 : public byte_boolean_option
567   { 570   {
568   public: 571   public:
569   using byte_boolean_option::byte_boolean_option; 572   using byte_boolean_option::byte_boolean_option;
570   using byte_boolean_option::operator=; 573   using byte_boolean_option::operator=;
571   574  
572   /// Return the protocol level. 575   /// Return the protocol level.
573   static int level() noexcept; 576   static int level() noexcept;
574   577  
575   /// Return the option name. 578   /// Return the option name.
576   static int name() noexcept; 579   static int name() noexcept;
577   }; 580   };
578   581  
579   /** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP). 582   /** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
580   583  
581   @par Example 584   @par Example
582   @code 585   @code
583   sock.set_option( socket_option::multicast_loop_v6( true ) ); 586   sock.set_option( socket_option::multicast_loop_v6( true ) );
584   @endcode 587   @endcode
585   */ 588   */
586   class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option 589   class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option
587   { 590   {
588   public: 591   public:
589   using boolean_option::boolean_option; 592   using boolean_option::boolean_option;
590   using boolean_option::operator=; 593   using boolean_option::operator=;
591   594  
592   /// Return the protocol level. 595   /// Return the protocol level.
593   static int level() noexcept; 596   static int level() noexcept;
594   597  
595   /// Return the option name. 598   /// Return the option name.
596   static int name() noexcept; 599   static int name() noexcept;
597   }; 600   };
598   601  
599   /** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL). 602   /** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
600   603  
601   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD) 604   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD)
602   reject the four-byte form with `EINVAL`. Linux accepts either size. 605   reject the four-byte form with `EINVAL`. Linux accepts either size.
603   Values are truncated to the 0–255 range. 606   Values are truncated to the 0–255 range.
604   607  
605   @par Example 608   @par Example
606   @code 609   @code
607   sock.set_option( socket_option::multicast_hops_v4( 4 ) ); 610   sock.set_option( socket_option::multicast_hops_v4( 4 ) );
608   @endcode 611   @endcode
609   */ 612   */
610   class BOOST_COROSIO_DECL multicast_hops_v4 : public byte_integer_option 613   class BOOST_COROSIO_DECL multicast_hops_v4 : public byte_integer_option
611   { 614   {
612   public: 615   public:
613   using byte_integer_option::byte_integer_option; 616   using byte_integer_option::byte_integer_option;
614   using byte_integer_option::operator=; 617   using byte_integer_option::operator=;
615   618  
616   /// Return the protocol level. 619   /// Return the protocol level.
617   static int level() noexcept; 620   static int level() noexcept;
618   621  
619   /// Return the option name. 622   /// Return the option name.
620   static int name() noexcept; 623   static int name() noexcept;
621   }; 624   };
622   625  
623   /** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS). 626   /** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
624   627  
625   @par Example 628   @par Example
626   @code 629   @code
627   sock.set_option( socket_option::multicast_hops_v6( 4 ) ); 630   sock.set_option( socket_option::multicast_hops_v6( 4 ) );
628   @endcode 631   @endcode
629   */ 632   */
630   class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option 633   class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option
631   { 634   {
632   public: 635   public:
633   using integer_option::integer_option; 636   using integer_option::integer_option;
634   using integer_option::operator=; 637   using integer_option::operator=;
635   638  
636   /// Return the protocol level. 639   /// Return the protocol level.
637   static int level() noexcept; 640   static int level() noexcept;
638   641  
639   /// Return the option name. 642   /// Return the option name.
640   static int name() noexcept; 643   static int name() noexcept;
641   }; 644   };
642   645  
643   /** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF). 646   /** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
644   647  
645   @par Example 648   @par Example
646   @code 649   @code
647   sock.set_option( socket_option::multicast_interface_v6( 1 ) ); 650   sock.set_option( socket_option::multicast_interface_v6( 1 ) );
648   @endcode 651   @endcode
649   */ 652   */
650   class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option 653   class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option
651   { 654   {
652   public: 655   public:
653   using integer_option::integer_option; 656   using integer_option::integer_option;
654   using integer_option::operator=; 657   using integer_option::operator=;
655   658  
656   /// Return the protocol level. 659   /// Return the protocol level.
657   static int level() noexcept; 660   static int level() noexcept;
658   661  
659   /// Return the option name. 662   /// Return the option name.
660   static int name() noexcept; 663   static int name() noexcept;
661   }; 664   };
662   665  
663   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP). 666   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
664   667  
665   @par Example 668   @par Example
666   @code 669   @code
667   sock.set_option( socket_option::join_group_v4( 670   sock.set_option( socket_option::join_group_v4(
668   ipv4_address( "239.255.0.1" ) ) ); 671   ipv4_address( "239.255.0.1" ) ) );
669   @endcode 672   @endcode
670   */ 673   */
671   class BOOST_COROSIO_DECL join_group_v4 674   class BOOST_COROSIO_DECL join_group_v4
672   { 675   {
673   static constexpr std::size_t max_storage_ = 8; 676   static constexpr std::size_t max_storage_ = 8;
674   alignas(4) unsigned char storage_[max_storage_]{}; 677   alignas(4) unsigned char storage_[max_storage_]{};
675   678  
676   public: 679   public:
677   /// Construct with default values. 680   /// Construct with default values.
678   join_group_v4() noexcept = default; 681   join_group_v4() noexcept = default;
679   682  
680   /** Construct with a group and optional interface address. 683   /** Construct with a group and optional interface address.
681   684  
682   @param group The multicast group address to join. 685   @param group The multicast group address to join.
683   @param iface The local interface to use (default: any). 686   @param iface The local interface to use (default: any).
684   */ 687   */
685   join_group_v4( 688   join_group_v4(
686   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept; 689   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
687   690  
688   /// Return the protocol level. 691   /// Return the protocol level.
689   static int level() noexcept; 692   static int level() noexcept;
690   693  
691   /// Return the option name. 694   /// Return the option name.
692   static int name() noexcept; 695   static int name() noexcept;
693   696  
694   /// Return a pointer to the underlying storage. 697   /// Return a pointer to the underlying storage.
695   void* data() noexcept 698   void* data() noexcept
696   { 699   {
697   return storage_; 700   return storage_;
698   } 701   }
699   702  
700   /// Return a pointer to the underlying storage. 703   /// Return a pointer to the underlying storage.
HITCBC 701   4 void const* data() const noexcept 704   4 void const* data() const noexcept
702   { 705   {
HITCBC 703   4 return storage_; 706   4 return storage_;
704   } 707   }
705   708  
706   /// Return the size of the underlying storage. 709   /// Return the size of the underlying storage.
707   std::size_t size() const noexcept; 710   std::size_t size() const noexcept;
708   711  
709   /// No-op resize. 712   /// No-op resize.
710   void resize(std::size_t) noexcept {} 713   void resize(std::size_t) noexcept {}
711   }; 714   };
712   715  
713   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP). 716   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
714   717  
715   @par Example 718   @par Example
716   @code 719   @code
717   sock.set_option( socket_option::leave_group_v4( 720   sock.set_option( socket_option::leave_group_v4(
718   ipv4_address( "239.255.0.1" ) ) ); 721   ipv4_address( "239.255.0.1" ) ) );
719   @endcode 722   @endcode
720   */ 723   */
721   class BOOST_COROSIO_DECL leave_group_v4 724   class BOOST_COROSIO_DECL leave_group_v4
722   { 725   {
723   static constexpr std::size_t max_storage_ = 8; 726   static constexpr std::size_t max_storage_ = 8;
724   alignas(4) unsigned char storage_[max_storage_]{}; 727   alignas(4) unsigned char storage_[max_storage_]{};
725   728  
726   public: 729   public:
727   /// Construct with default values. 730   /// Construct with default values.
728   leave_group_v4() noexcept = default; 731   leave_group_v4() noexcept = default;
729   732  
730   /** Construct with a group and optional interface address. 733   /** Construct with a group and optional interface address.
731   734  
732   @param group The multicast group address to leave. 735   @param group The multicast group address to leave.
733   @param iface The local interface (default: any). 736   @param iface The local interface (default: any).
734   */ 737   */
735   leave_group_v4( 738   leave_group_v4(
736   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept; 739   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
737   740  
738   /// Return the protocol level. 741   /// Return the protocol level.
739   static int level() noexcept; 742   static int level() noexcept;
740   743  
741   /// Return the option name. 744   /// Return the option name.
742   static int name() noexcept; 745   static int name() noexcept;
743   746  
744   /// Return a pointer to the underlying storage. 747   /// Return a pointer to the underlying storage.
745   void* data() noexcept 748   void* data() noexcept
746   { 749   {
747   return storage_; 750   return storage_;
748   } 751   }
749   752  
750   /// Return a pointer to the underlying storage. 753   /// Return a pointer to the underlying storage.
HITCBC 751   2 void const* data() const noexcept 754   2 void const* data() const noexcept
752   { 755   {
HITCBC 753   2 return storage_; 756   2 return storage_;
754   } 757   }
755   758  
756   /// Return the size of the underlying storage. 759   /// Return the size of the underlying storage.
757   std::size_t size() const noexcept; 760   std::size_t size() const noexcept;
758   761  
759   /// No-op resize. 762   /// No-op resize.
760   void resize(std::size_t) noexcept {} 763   void resize(std::size_t) noexcept {}
761   }; 764   };
762   765  
763   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP). 766   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
764   767  
765   @par Example 768   @par Example
766   @code 769   @code
767   sock.set_option( socket_option::join_group_v6( 770   sock.set_option( socket_option::join_group_v6(
768   ipv6_address( "ff02::1" ), 0 ) ); 771   ipv6_address( "ff02::1" ), 0 ) );
769   @endcode 772   @endcode
770   */ 773   */
771   class BOOST_COROSIO_DECL join_group_v6 774   class BOOST_COROSIO_DECL join_group_v6
772   { 775   {
773   static constexpr std::size_t max_storage_ = 20; 776   static constexpr std::size_t max_storage_ = 20;
774   alignas(4) unsigned char storage_[max_storage_]{}; 777   alignas(4) unsigned char storage_[max_storage_]{};
775   778  
776   public: 779   public:
777   /// Construct with default values. 780   /// Construct with default values.
778   join_group_v6() noexcept = default; 781   join_group_v6() noexcept = default;
779   782  
780   /** Construct with a group and optional interface index. 783   /** Construct with a group and optional interface index.
781   784  
782   @param group The multicast group address to join. 785   @param group The multicast group address to join.
783   @param if_index The interface index (0 = kernel chooses). 786   @param if_index The interface index (0 = kernel chooses).
784   */ 787   */
785   join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept; 788   join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
786   789  
787   /// Return the protocol level. 790   /// Return the protocol level.
788   static int level() noexcept; 791   static int level() noexcept;
789   792  
790   /// Return the option name. 793   /// Return the option name.
791   static int name() noexcept; 794   static int name() noexcept;
792   795  
793   /// Return a pointer to the underlying storage. 796   /// Return a pointer to the underlying storage.
794   void* data() noexcept 797   void* data() noexcept
795   { 798   {
796   return storage_; 799   return storage_;
797   } 800   }
798   801  
799   /// Return a pointer to the underlying storage. 802   /// Return a pointer to the underlying storage.
HITCBC 800   2 void const* data() const noexcept 803   2 void const* data() const noexcept
801   { 804   {
HITCBC 802   2 return storage_; 805   2 return storage_;
803   } 806   }
804   807  
805   /// Return the size of the underlying storage. 808   /// Return the size of the underlying storage.
806   std::size_t size() const noexcept; 809   std::size_t size() const noexcept;
807   810  
808   /// No-op resize. 811   /// No-op resize.
809   void resize(std::size_t) noexcept {} 812   void resize(std::size_t) noexcept {}
810   }; 813   };
811   814  
812   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP). 815   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
813   816  
814   @par Example 817   @par Example
815   @code 818   @code
816   sock.set_option( socket_option::leave_group_v6( 819   sock.set_option( socket_option::leave_group_v6(
817   ipv6_address( "ff02::1" ), 0 ) ); 820   ipv6_address( "ff02::1" ), 0 ) );
818   @endcode 821   @endcode
819   */ 822   */
820   class BOOST_COROSIO_DECL leave_group_v6 823   class BOOST_COROSIO_DECL leave_group_v6
821   { 824   {
822   static constexpr std::size_t max_storage_ = 20; 825   static constexpr std::size_t max_storage_ = 20;
823   alignas(4) unsigned char storage_[max_storage_]{}; 826   alignas(4) unsigned char storage_[max_storage_]{};
824   827  
825   public: 828   public:
826   /// Construct with default values. 829   /// Construct with default values.
827   leave_group_v6() noexcept = default; 830   leave_group_v6() noexcept = default;
828   831  
829   /** Construct with a group and optional interface index. 832   /** Construct with a group and optional interface index.
830   833  
831   @param group The multicast group address to leave. 834   @param group The multicast group address to leave.
832   @param if_index The interface index (0 = kernel chooses). 835   @param if_index The interface index (0 = kernel chooses).
833   */ 836   */
834   leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept; 837   leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
835   838  
836   /// Return the protocol level. 839   /// Return the protocol level.
837   static int level() noexcept; 840   static int level() noexcept;
838   841  
839   /// Return the option name. 842   /// Return the option name.
840   static int name() noexcept; 843   static int name() noexcept;
841   844  
842   /// Return a pointer to the underlying storage. 845   /// Return a pointer to the underlying storage.
843   void* data() noexcept 846   void* data() noexcept
844   { 847   {
845   return storage_; 848   return storage_;
846   } 849   }
847   850  
848   /// Return a pointer to the underlying storage. 851   /// Return a pointer to the underlying storage.
HITCBC 849   2 void const* data() const noexcept 852   2 void const* data() const noexcept
850   { 853   {
HITCBC 851   2 return storage_; 854   2 return storage_;
852   } 855   }
853   856  
854   /// Return the size of the underlying storage. 857   /// Return the size of the underlying storage.
855   std::size_t size() const noexcept; 858   std::size_t size() const noexcept;
856   859  
857   /// No-op resize. 860   /// No-op resize.
858   void resize(std::size_t) noexcept {} 861   void resize(std::size_t) noexcept {}
859   }; 862   };
860   863  
861   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF). 864   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
862   865  
863   Unlike the integer-based `multicast_interface_v6`, this option 866   Unlike the integer-based `multicast_interface_v6`, this option
864   takes an `ipv4_address` identifying the local interface. 867   takes an `ipv4_address` identifying the local interface.
865   868  
866   @par Example 869   @par Example
867   @code 870   @code
868   sock.set_option( socket_option::multicast_interface_v4( 871   sock.set_option( socket_option::multicast_interface_v4(
869   ipv4_address( "192.168.1.1" ) ) ); 872   ipv4_address( "192.168.1.1" ) ) );
870   @endcode 873   @endcode
871   */ 874   */
872   class BOOST_COROSIO_DECL multicast_interface_v4 875   class BOOST_COROSIO_DECL multicast_interface_v4
873   { 876   {
874   static constexpr std::size_t max_storage_ = 4; 877   static constexpr std::size_t max_storage_ = 4;
875   alignas(4) unsigned char storage_[max_storage_]{}; 878   alignas(4) unsigned char storage_[max_storage_]{};
876   879  
877   public: 880   public:
878   /// Construct with default values (INADDR_ANY). 881   /// Construct with default values (INADDR_ANY).
879   multicast_interface_v4() noexcept = default; 882   multicast_interface_v4() noexcept = default;
880   883  
881   /** Construct with an interface address. 884   /** Construct with an interface address.
882   885  
883   @param iface The local interface address. 886   @param iface The local interface address.
884   */ 887   */
885   explicit multicast_interface_v4(ipv4_address iface) noexcept; 888   explicit multicast_interface_v4(ipv4_address iface) noexcept;
886   889  
887   /// Return the protocol level. 890   /// Return the protocol level.
888   static int level() noexcept; 891   static int level() noexcept;
889   892  
890   /// Return the option name. 893   /// Return the option name.
891   static int name() noexcept; 894   static int name() noexcept;
892   895  
893   /// Return a pointer to the underlying storage. 896   /// Return a pointer to the underlying storage.
894   void* data() noexcept 897   void* data() noexcept
895   { 898   {
896   return storage_; 899   return storage_;
897   } 900   }
898   901  
899   /// Return a pointer to the underlying storage. 902   /// Return a pointer to the underlying storage.
HITCBC 900   2 void const* data() const noexcept 903   2 void const* data() const noexcept
901   { 904   {
HITCBC 902   2 return storage_; 905   2 return storage_;
903   } 906   }
904   907  
905   /// Return the size of the underlying storage. 908   /// Return the size of the underlying storage.
906   std::size_t size() const noexcept; 909   std::size_t size() const noexcept;
907   910  
908   /// No-op resize. 911   /// No-op resize.
909   void resize(std::size_t) noexcept {} 912   void resize(std::size_t) noexcept {}
910   }; 913   };
911   914  
912   } // namespace boost::corosio::socket_option 915   } // namespace boost::corosio::socket_option
913   916  
914   #endif // BOOST_COROSIO_SOCKET_OPTION_HPP 917   #endif // BOOST_COROSIO_SOCKET_OPTION_HPP