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   469 explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {} 55   541 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   78 void* data() noexcept 83   89 void* data() noexcept
84   { 84   {
HITCBC 85   78 return &value_; 85   89 return &value_;
86   } 86   }
87   87  
88   /// Return a pointer to the underlying storage. 88   /// Return a pointer to the underlying storage.
HITCBC 89   463 void const* data() const noexcept 89   535 void const* data() const noexcept
90   { 90   {
HITCBC 91   463 return &value_; 91   535 return &value_;
92   } 92   }
93   93  
94   /// Return the size of the underlying storage. 94   /// Return the size of the underlying storage.
HITCBC 95   541 std::size_t size() const noexcept 95   624 std::size_t size() const noexcept
96   { 96   {
HITCBC 97   541 return sizeof(value_); 97   624 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   65 explicit integer_option(int v) noexcept : value_(v) {} 130   79 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   60 int value() const noexcept
141   { 141   {
HITCBC 142   46 return value_; 142   60 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   58 void* data() noexcept
147   { 147   {
HITCBC 148   44 return &value_; 148   58 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   75 void const* data() const noexcept
153   { 153   {
HITCBC 154   61 return &value_; 154   75 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   133 std::size_t size() const noexcept
159   { 159   {
HITCBC 160   105 return sizeof(value_); 160   133 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   58 void resize(std::size_t s) noexcept
168   { 168   {
HITCBC 169   44 if (s == sizeof(char)) 169   58 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   58 }
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   bool disabled = nd.value(); // true: Nagle's algorithm is off 306   bool disabled = nd.value(); // true: Nagle's algorithm is off
307   @endcode 307   @endcode
308   */ 308   */
309   class BOOST_COROSIO_DECL no_delay : public boolean_option 309   class BOOST_COROSIO_DECL no_delay : public boolean_option
310   { 310   {
311   public: 311   public:
312   using boolean_option::boolean_option; 312   using boolean_option::boolean_option;
313   using boolean_option::operator=; 313   using boolean_option::operator=;
314   314  
315   /// Return the protocol level. 315   /// Return the protocol level.
316   static int level() noexcept; 316   static int level() noexcept;
317   317  
318   /// Return the option name. 318   /// Return the option name.
319   static int name() noexcept; 319   static int name() noexcept;
320   }; 320   };
321   321  
322   /** Enable periodic keepalive probes (SO_KEEPALIVE). 322   /** Enable periodic keepalive probes (SO_KEEPALIVE).
323   323  
324   @par Example 324   @par Example
325   @code 325   @code
326   sock.set_option( socket_option::keep_alive( true ) ); 326   sock.set_option( socket_option::keep_alive( true ) );
327   @endcode 327   @endcode
328   */ 328   */
329   class BOOST_COROSIO_DECL keep_alive : public boolean_option 329   class BOOST_COROSIO_DECL keep_alive : public boolean_option
330   { 330   {
331   public: 331   public:
332   using boolean_option::boolean_option; 332   using boolean_option::boolean_option;
333   using boolean_option::operator=; 333   using boolean_option::operator=;
334   334  
335   /// Return the protocol level. 335   /// Return the protocol level.
336   static int level() noexcept; 336   static int level() noexcept;
337   337  
338   /// Return the option name. 338   /// Return the option name.
339   static int name() noexcept; 339   static int name() noexcept;
340   }; 340   };
341   341  
342   /** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY). 342   /** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
343   343  
344   When enabled, the socket only accepts IPv6 connections. 344   When enabled, the socket only accepts IPv6 connections.
345   When disabled, the socket accepts both IPv4 and IPv6 345   When disabled, the socket accepts both IPv4 and IPv6
346   connections (dual-stack mode). 346   connections (dual-stack mode).
347   347  
348   @par Example 348   @par Example
349   @code 349   @code
350   sock.set_option( socket_option::v6_only( true ) ); 350   sock.set_option( socket_option::v6_only( true ) );
351   @endcode 351   @endcode
352   */ 352   */
353   class BOOST_COROSIO_DECL v6_only : public boolean_option 353   class BOOST_COROSIO_DECL v6_only : public boolean_option
354   { 354   {
355   public: 355   public:
356   using boolean_option::boolean_option; 356   using boolean_option::boolean_option;
357   using boolean_option::operator=; 357   using boolean_option::operator=;
358   358  
359   /// Return the protocol level. 359   /// Return the protocol level.
360   static int level() noexcept; 360   static int level() noexcept;
361   361  
362   /// Return the option name. 362   /// Return the option name.
363   static int name() noexcept; 363   static int name() noexcept;
364   }; 364   };
365   365  
366   /** Allow local address reuse (SO_REUSEADDR). 366   /** Allow local address reuse (SO_REUSEADDR).
367   367  
368   @par Example 368   @par Example
369   @code 369   @code
370   acc.set_option( socket_option::reuse_address( true ) ); 370   acc.set_option( socket_option::reuse_address( true ) );
371   @endcode 371   @endcode
372   */ 372   */
373   class BOOST_COROSIO_DECL reuse_address : public boolean_option 373   class BOOST_COROSIO_DECL reuse_address : public boolean_option
374   { 374   {
375   public: 375   public:
376   using boolean_option::boolean_option; 376   using boolean_option::boolean_option;
377   using boolean_option::operator=; 377   using boolean_option::operator=;
378   378  
379   /// Return the protocol level. 379   /// Return the protocol level.
380   static int level() noexcept; 380   static int level() noexcept;
381   381  
382   /// Return the option name. 382   /// Return the option name.
383   static int name() noexcept; 383   static int name() noexcept;
384   }; 384   };
385   385  
386   /** Allow sending to broadcast addresses (SO_BROADCAST). 386   /** Allow sending to broadcast addresses (SO_BROADCAST).
387   387  
388   Required for UDP sockets that send to broadcast addresses 388   Required for UDP sockets that send to broadcast addresses
389   such as 255.255.255.255. Without this option, `send_to` 389   such as 255.255.255.255. Without this option, `send_to`
390   returns an error. 390   returns an error.
391   391  
392   @par Example 392   @par Example
393   @code 393   @code
394   udp_socket sock( ioc ); 394   udp_socket sock( ioc );
395   if ( auto ec = sock.open() ) 395   if ( auto ec = sock.open() )
396   return; 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` throws `std::system_error`. 416   `set_option` throws `std::system_error`.
417   417  
418   @par Example 418   @par Example
419   @code 419   @code
420   if ( auto ec = acc.open( tcp::v6() ) ) 420   if ( auto ec = acc.open( tcp::v6() ) )
421   return; 421   return;
422   acc.set_option( socket_option::reuse_port( true ) ); 422   acc.set_option( socket_option::reuse_port( true ) );
423   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) ) 423   if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
424   return; 424   return;
425   if ( auto ec = acc.listen() ) 425   if ( auto ec = acc.listen() )
426   return; 426   return;
427   @endcode 427   @endcode
428   */ 428   */
429   class BOOST_COROSIO_DECL reuse_port : public boolean_option 429   class BOOST_COROSIO_DECL reuse_port : public boolean_option
430   { 430   {
431   public: 431   public:
432   using boolean_option::boolean_option; 432   using boolean_option::boolean_option;
433   using boolean_option::operator=; 433   using boolean_option::operator=;
434   434  
435   /// Return the protocol level. 435   /// Return the protocol level.
436   static int level() noexcept; 436   static int level() noexcept;
437   437  
438   /// Return the option name. 438   /// Return the option name.
439   static int name() noexcept; 439   static int name() noexcept;
440   }; 440   };
441   441  
442   /** Set the receive buffer size (SO_RCVBUF). 442   /** Set the receive buffer size (SO_RCVBUF).
443   443  
444   @par Example 444   @par Example
445   @code 445   @code
446   sock.set_option( socket_option::receive_buffer_size( 65536 ) ); 446   sock.set_option( socket_option::receive_buffer_size( 65536 ) );
447   auto opt = sock.get_option<socket_option::receive_buffer_size>(); 447   auto opt = sock.get_option<socket_option::receive_buffer_size>();
448   int sz = opt.value(); 448   int sz = opt.value();
449   @endcode 449   @endcode
450   */ 450   */
451   class BOOST_COROSIO_DECL receive_buffer_size : public integer_option 451   class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
452   { 452   {
453   public: 453   public:
454   using integer_option::integer_option; 454   using integer_option::integer_option;
455   using integer_option::operator=; 455   using integer_option::operator=;
456   456  
457   /// Return the protocol level. 457   /// Return the protocol level.
458   static int level() noexcept; 458   static int level() noexcept;
459   459  
460   /// Return the option name. 460   /// Return the option name.
461   static int name() noexcept; 461   static int name() noexcept;
462   }; 462   };
463   463  
464   /** Set the send buffer size (SO_SNDBUF). 464   /** Set the send buffer size (SO_SNDBUF).
465   465  
466   @par Example 466   @par Example
467   @code 467   @code
468   sock.set_option( socket_option::send_buffer_size( 65536 ) ); 468   sock.set_option( socket_option::send_buffer_size( 65536 ) );
469   @endcode 469   @endcode
470   */ 470   */
471   class BOOST_COROSIO_DECL send_buffer_size : public integer_option 471   class BOOST_COROSIO_DECL send_buffer_size : public integer_option
472   { 472   {
473   public: 473   public:
474   using integer_option::integer_option; 474   using integer_option::integer_option;
475   using integer_option::operator=; 475   using integer_option::operator=;
476   476  
477   /// Return the protocol level. 477   /// Return the protocol level.
478   static int level() noexcept; 478   static int level() noexcept;
479   479  
480   /// Return the option name. 480   /// Return the option name.
481   static int name() noexcept; 481   static int name() noexcept;
482   }; 482   };
483   483  
484   /** The SO_LINGER socket option. 484   /** The SO_LINGER socket option.
485   485  
486   Controls behavior when closing a socket with unsent data. 486   Controls behavior when closing a socket with unsent data.
487   When enabled, `close()` blocks until pending data is sent 487   When enabled, `close()` blocks until pending data is sent
488   or the timeout expires. 488   or the timeout expires.
489   489  
490   @par Example 490   @par Example
491   @code 491   @code
492   sock.set_option( socket_option::linger( true, 5 ) ); 492   sock.set_option( socket_option::linger( true, 5 ) );
493   auto opt = sock.get_option<socket_option::linger>(); 493   auto opt = sock.get_option<socket_option::linger>();
494   if ( opt.enabled() ) 494   if ( opt.enabled() )
495   std::cout << "linger timeout: " << opt.timeout() << "s\n"; 495   std::cout << "linger timeout: " << opt.timeout() << "s\n";
496   @endcode 496   @endcode
497   */ 497   */
498   class BOOST_COROSIO_DECL linger 498   class BOOST_COROSIO_DECL linger
499   { 499   {
500   // Opaque storage for the platform's struct linger. 500   // Opaque storage for the platform's struct linger.
501   // POSIX: { int, int } = 8 bytes. 501   // POSIX: { int, int } = 8 bytes.
502   // Windows: { u_short, u_short } = 4 bytes. 502   // Windows: { u_short, u_short } = 4 bytes.
503   static constexpr std::size_t max_storage_ = 8; 503   static constexpr std::size_t max_storage_ = 8;
504   alignas(4) unsigned char storage_[max_storage_]{}; 504   alignas(4) unsigned char storage_[max_storage_]{};
505   505  
506   public: 506   public:
507   /// Construct with default values (disabled, zero timeout). 507   /// Construct with default values (disabled, zero timeout).
508   linger() noexcept = default; 508   linger() noexcept = default;
509   509  
510   /** Construct with explicit values. 510   /** Construct with explicit values.
511   511  
512   @param enabled `true` to enable linger behavior on close. 512   @param enabled `true` to enable linger behavior on close.
513   @param timeout The linger timeout in seconds. 513   @param timeout The linger timeout in seconds.
514   */ 514   */
515   linger(bool enabled, int timeout) noexcept; 515   linger(bool enabled, int timeout) noexcept;
516   516  
517   /// Return whether linger is enabled. 517   /// Return whether linger is enabled.
518   bool enabled() const noexcept; 518   bool enabled() const noexcept;
519   519  
520   /// Set whether linger is enabled. 520   /// Set whether linger is enabled.
521   void enabled(bool v) noexcept; 521   void enabled(bool v) noexcept;
522   522  
523   /// Return the linger timeout in seconds. 523   /// Return the linger timeout in seconds.
524   int timeout() const noexcept; 524   int timeout() const noexcept;
525   525  
526   /// Set the linger timeout in seconds. 526   /// Set the linger timeout in seconds.
527   void timeout(int v) noexcept; 527   void timeout(int v) noexcept;
528   528  
529   /// Return the protocol level. 529   /// Return the protocol level.
530   static int level() noexcept; 530   static int level() noexcept;
531   531  
532   /// Return the option name. 532   /// Return the option name.
533   static int name() noexcept; 533   static int name() noexcept;
534   534  
535   /// Return a pointer to the underlying storage. 535   /// Return a pointer to the underlying storage.
HITCBC 536   12 void* data() noexcept 536   12 void* data() noexcept
537   { 537   {
HITCBC 538   12 return storage_; 538   12 return storage_;
539   } 539   }
540   540  
541   /// Return a pointer to the underlying storage. 541   /// Return a pointer to the underlying storage.
HITCBC 542   152 void const* data() const noexcept 542   195 void const* data() const noexcept
543   { 543   {
HITCBC 544   152 return storage_; 544   195 return storage_;
545   } 545   }
546   546  
547   /// Return the size of the underlying storage. 547   /// Return the size of the underlying storage.
548   std::size_t size() const noexcept; 548   std::size_t size() const noexcept;
549   549  
550   /** Normalize after `getsockopt`. 550   /** Normalize after `getsockopt`.
551   551  
552   No-op — `struct linger` is always returned at full size. 552   No-op — `struct linger` is always returned at full size.
553   553  
554   @param s The number of bytes actually written by `getsockopt`. 554   @param s The number of bytes actually written by `getsockopt`.
555   */ 555   */
HITCBC 556   12 void resize(std::size_t) noexcept {} 556   12 void resize(std::size_t) noexcept {}
557   }; 557   };
558   558  
559   /** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP). 559   /** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
560   560  
561   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD) 561   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD)
562   reject the four-byte form with `EINVAL`. Linux accepts either size. 562   reject the four-byte form with `EINVAL`. Linux accepts either size.
563   563  
564   @par Example 564   @par Example
565   @code 565   @code
566   sock.set_option( socket_option::multicast_loop_v4( true ) ); 566   sock.set_option( socket_option::multicast_loop_v4( true ) );
567   @endcode 567   @endcode
568   */ 568   */
569   class BOOST_COROSIO_DECL multicast_loop_v4 : public byte_boolean_option 569   class BOOST_COROSIO_DECL multicast_loop_v4 : public byte_boolean_option
570   { 570   {
571   public: 571   public:
572   using byte_boolean_option::byte_boolean_option; 572   using byte_boolean_option::byte_boolean_option;
573   using byte_boolean_option::operator=; 573   using byte_boolean_option::operator=;
574   574  
575   /// Return the protocol level. 575   /// Return the protocol level.
576   static int level() noexcept; 576   static int level() noexcept;
577   577  
578   /// Return the option name. 578   /// Return the option name.
579   static int name() noexcept; 579   static int name() noexcept;
580   }; 580   };
581   581  
582   /** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP). 582   /** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
583   583  
584   @par Example 584   @par Example
585   @code 585   @code
586   sock.set_option( socket_option::multicast_loop_v6( true ) ); 586   sock.set_option( socket_option::multicast_loop_v6( true ) );
587   @endcode 587   @endcode
588   */ 588   */
589   class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option 589   class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option
590   { 590   {
591   public: 591   public:
592   using boolean_option::boolean_option; 592   using boolean_option::boolean_option;
593   using boolean_option::operator=; 593   using boolean_option::operator=;
594   594  
595   /// Return the protocol level. 595   /// Return the protocol level.
596   static int level() noexcept; 596   static int level() noexcept;
597   597  
598   /// Return the option name. 598   /// Return the option name.
599   static int name() noexcept; 599   static int name() noexcept;
600   }; 600   };
601   601  
602   /** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL). 602   /** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
603   603  
604   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD) 604   Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD)
605   reject the four-byte form with `EINVAL`. Linux accepts either size. 605   reject the four-byte form with `EINVAL`. Linux accepts either size.
606   Values are truncated to the 0–255 range. 606   Values are truncated to the 0–255 range.
607   607  
608   @par Example 608   @par Example
609   @code 609   @code
610   sock.set_option( socket_option::multicast_hops_v4( 4 ) ); 610   sock.set_option( socket_option::multicast_hops_v4( 4 ) );
611   @endcode 611   @endcode
612   */ 612   */
613   class BOOST_COROSIO_DECL multicast_hops_v4 : public byte_integer_option 613   class BOOST_COROSIO_DECL multicast_hops_v4 : public byte_integer_option
614   { 614   {
615   public: 615   public:
616   using byte_integer_option::byte_integer_option; 616   using byte_integer_option::byte_integer_option;
617   using byte_integer_option::operator=; 617   using byte_integer_option::operator=;
618   618  
619   /// Return the protocol level. 619   /// Return the protocol level.
620   static int level() noexcept; 620   static int level() noexcept;
621   621  
622   /// Return the option name. 622   /// Return the option name.
623   static int name() noexcept; 623   static int name() noexcept;
624   }; 624   };
625   625  
626   /** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS). 626   /** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
627   627  
628   @par Example 628   @par Example
629   @code 629   @code
630   sock.set_option( socket_option::multicast_hops_v6( 4 ) ); 630   sock.set_option( socket_option::multicast_hops_v6( 4 ) );
631   @endcode 631   @endcode
632   */ 632   */
633   class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option 633   class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option
634   { 634   {
635   public: 635   public:
636   using integer_option::integer_option; 636   using integer_option::integer_option;
637   using integer_option::operator=; 637   using integer_option::operator=;
638   638  
639   /// Return the protocol level. 639   /// Return the protocol level.
640   static int level() noexcept; 640   static int level() noexcept;
641   641  
642   /// Return the option name. 642   /// Return the option name.
643   static int name() noexcept; 643   static int name() noexcept;
644   }; 644   };
645   645  
646   /** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF). 646   /** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
647   647  
648   @par Example 648   @par Example
649   @code 649   @code
650   sock.set_option( socket_option::multicast_interface_v6( 1 ) ); 650   sock.set_option( socket_option::multicast_interface_v6( 1 ) );
651   @endcode 651   @endcode
652   */ 652   */
653   class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option 653   class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option
654   { 654   {
655   public: 655   public:
656   using integer_option::integer_option; 656   using integer_option::integer_option;
657   using integer_option::operator=; 657   using integer_option::operator=;
658   658  
659   /// Return the protocol level. 659   /// Return the protocol level.
660   static int level() noexcept; 660   static int level() noexcept;
661   661  
662   /// Return the option name. 662   /// Return the option name.
663   static int name() noexcept; 663   static int name() noexcept;
664   }; 664   };
665   665  
666   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP). 666   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
667   667  
668   @par Example 668   @par Example
669   @code 669   @code
670   sock.set_option( socket_option::join_group_v4( 670   sock.set_option( socket_option::join_group_v4(
671   ipv4_address( "239.255.0.1" ) ) ); 671   ipv4_address( "239.255.0.1" ) ) );
672   @endcode 672   @endcode
673   */ 673   */
674   class BOOST_COROSIO_DECL join_group_v4 674   class BOOST_COROSIO_DECL join_group_v4
675   { 675   {
676   static constexpr std::size_t max_storage_ = 8; 676   static constexpr std::size_t max_storage_ = 8;
677   alignas(4) unsigned char storage_[max_storage_]{}; 677   alignas(4) unsigned char storage_[max_storage_]{};
678   678  
679   public: 679   public:
680   /// Construct with default values. 680   /// Construct with default values.
681   join_group_v4() noexcept = default; 681   join_group_v4() noexcept = default;
682   682  
683   /** Construct with a group and optional interface address. 683   /** Construct with a group and optional interface address.
684   684  
685   @param group The multicast group address to join. 685   @param group The multicast group address to join.
686   @param iface The local interface to use (default: any). 686   @param iface The local interface to use (default: any).
687   */ 687   */
688   join_group_v4( 688   join_group_v4(
689   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept; 689   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
690   690  
691   /// Return the protocol level. 691   /// Return the protocol level.
692   static int level() noexcept; 692   static int level() noexcept;
693   693  
694   /// Return the option name. 694   /// Return the option name.
695   static int name() noexcept; 695   static int name() noexcept;
696   696  
697   /// Return a pointer to the underlying storage. 697   /// Return a pointer to the underlying storage.
698   void* data() noexcept 698   void* data() noexcept
699   { 699   {
700   return storage_; 700   return storage_;
701   } 701   }
702   702  
703   /// Return a pointer to the underlying storage. 703   /// Return a pointer to the underlying storage.
HITCBC 704   4 void const* data() const noexcept 704   4 void const* data() const noexcept
705   { 705   {
HITCBC 706   4 return storage_; 706   4 return storage_;
707   } 707   }
708   708  
709   /// Return the size of the underlying storage. 709   /// Return the size of the underlying storage.
710   std::size_t size() const noexcept; 710   std::size_t size() const noexcept;
711   711  
712   /// No-op resize. 712   /// No-op resize.
713   void resize(std::size_t) noexcept {} 713   void resize(std::size_t) noexcept {}
714   }; 714   };
715   715  
716   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP). 716   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
717   717  
718   @par Example 718   @par Example
719   @code 719   @code
720   sock.set_option( socket_option::leave_group_v4( 720   sock.set_option( socket_option::leave_group_v4(
721   ipv4_address( "239.255.0.1" ) ) ); 721   ipv4_address( "239.255.0.1" ) ) );
722   @endcode 722   @endcode
723   */ 723   */
724   class BOOST_COROSIO_DECL leave_group_v4 724   class BOOST_COROSIO_DECL leave_group_v4
725   { 725   {
726   static constexpr std::size_t max_storage_ = 8; 726   static constexpr std::size_t max_storage_ = 8;
727   alignas(4) unsigned char storage_[max_storage_]{}; 727   alignas(4) unsigned char storage_[max_storage_]{};
728   728  
729   public: 729   public:
730   /// Construct with default values. 730   /// Construct with default values.
731   leave_group_v4() noexcept = default; 731   leave_group_v4() noexcept = default;
732   732  
733   /** Construct with a group and optional interface address. 733   /** Construct with a group and optional interface address.
734   734  
735   @param group The multicast group address to leave. 735   @param group The multicast group address to leave.
736   @param iface The local interface (default: any). 736   @param iface The local interface (default: any).
737   */ 737   */
738   leave_group_v4( 738   leave_group_v4(
739   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept; 739   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
740   740  
741   /// Return the protocol level. 741   /// Return the protocol level.
742   static int level() noexcept; 742   static int level() noexcept;
743   743  
744   /// Return the option name. 744   /// Return the option name.
745   static int name() noexcept; 745   static int name() noexcept;
746   746  
747   /// Return a pointer to the underlying storage. 747   /// Return a pointer to the underlying storage.
748   void* data() noexcept 748   void* data() noexcept
749   { 749   {
750   return storage_; 750   return storage_;
751   } 751   }
752   752  
753   /// Return a pointer to the underlying storage. 753   /// Return a pointer to the underlying storage.
HITCBC 754   2 void const* data() const noexcept 754   2 void const* data() const noexcept
755   { 755   {
HITCBC 756   2 return storage_; 756   2 return storage_;
757   } 757   }
758   758  
759   /// Return the size of the underlying storage. 759   /// Return the size of the underlying storage.
760   std::size_t size() const noexcept; 760   std::size_t size() const noexcept;
761   761  
762   /// No-op resize. 762   /// No-op resize.
763   void resize(std::size_t) noexcept {} 763   void resize(std::size_t) noexcept {}
764   }; 764   };
765   765  
766   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP). 766   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
767   767  
768   @par Example 768   @par Example
769   @code 769   @code
770   sock.set_option( socket_option::join_group_v6( 770   sock.set_option( socket_option::join_group_v6(
771   ipv6_address( "ff02::1" ), 0 ) ); 771   ipv6_address( "ff02::1" ), 0 ) );
772   @endcode 772   @endcode
773   */ 773   */
774   class BOOST_COROSIO_DECL join_group_v6 774   class BOOST_COROSIO_DECL join_group_v6
775   { 775   {
776   static constexpr std::size_t max_storage_ = 20; 776   static constexpr std::size_t max_storage_ = 20;
777   alignas(4) unsigned char storage_[max_storage_]{}; 777   alignas(4) unsigned char storage_[max_storage_]{};
778   778  
779   public: 779   public:
780   /// Construct with default values. 780   /// Construct with default values.
781   join_group_v6() noexcept = default; 781   join_group_v6() noexcept = default;
782   782  
783   /** Construct with a group and optional interface index. 783   /** Construct with a group and optional interface index.
784   784  
785   @param group The multicast group address to join. 785   @param group The multicast group address to join.
786   @param if_index The interface index (0 = kernel chooses). 786   @param if_index The interface index (0 = kernel chooses).
787   */ 787   */
788   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;
789   789  
790   /// Return the protocol level. 790   /// Return the protocol level.
791   static int level() noexcept; 791   static int level() noexcept;
792   792  
793   /// Return the option name. 793   /// Return the option name.
794   static int name() noexcept; 794   static int name() noexcept;
795   795  
796   /// Return a pointer to the underlying storage. 796   /// Return a pointer to the underlying storage.
797   void* data() noexcept 797   void* data() noexcept
798   { 798   {
799   return storage_; 799   return storage_;
800   } 800   }
801   801  
802   /// Return a pointer to the underlying storage. 802   /// Return a pointer to the underlying storage.
HITCBC 803   2 void const* data() const noexcept 803   2 void const* data() const noexcept
804   { 804   {
HITCBC 805   2 return storage_; 805   2 return storage_;
806   } 806   }
807   807  
808   /// Return the size of the underlying storage. 808   /// Return the size of the underlying storage.
809   std::size_t size() const noexcept; 809   std::size_t size() const noexcept;
810   810  
811   /// No-op resize. 811   /// No-op resize.
812   void resize(std::size_t) noexcept {} 812   void resize(std::size_t) noexcept {}
813   }; 813   };
814   814  
815   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP). 815   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
816   816  
817   @par Example 817   @par Example
818   @code 818   @code
819   sock.set_option( socket_option::leave_group_v6( 819   sock.set_option( socket_option::leave_group_v6(
820   ipv6_address( "ff02::1" ), 0 ) ); 820   ipv6_address( "ff02::1" ), 0 ) );
821   @endcode 821   @endcode
822   */ 822   */
823   class BOOST_COROSIO_DECL leave_group_v6 823   class BOOST_COROSIO_DECL leave_group_v6
824   { 824   {
825   static constexpr std::size_t max_storage_ = 20; 825   static constexpr std::size_t max_storage_ = 20;
826   alignas(4) unsigned char storage_[max_storage_]{}; 826   alignas(4) unsigned char storage_[max_storage_]{};
827   827  
828   public: 828   public:
829   /// Construct with default values. 829   /// Construct with default values.
830   leave_group_v6() noexcept = default; 830   leave_group_v6() noexcept = default;
831   831  
832   /** Construct with a group and optional interface index. 832   /** Construct with a group and optional interface index.
833   833  
834   @param group The multicast group address to leave. 834   @param group The multicast group address to leave.
835   @param if_index The interface index (0 = kernel chooses). 835   @param if_index The interface index (0 = kernel chooses).
836   */ 836   */
837   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;
838   838  
839   /// Return the protocol level. 839   /// Return the protocol level.
840   static int level() noexcept; 840   static int level() noexcept;
841   841  
842   /// Return the option name. 842   /// Return the option name.
843   static int name() noexcept; 843   static int name() noexcept;
844   844  
845   /// Return a pointer to the underlying storage. 845   /// Return a pointer to the underlying storage.
846   void* data() noexcept 846   void* data() noexcept
847   { 847   {
848   return storage_; 848   return storage_;
849   } 849   }
850   850  
851   /// Return a pointer to the underlying storage. 851   /// Return a pointer to the underlying storage.
HITCBC 852   2 void const* data() const noexcept 852   2 void const* data() const noexcept
853   { 853   {
HITCBC 854   2 return storage_; 854   2 return storage_;
855   } 855   }
856   856  
857   /// Return the size of the underlying storage. 857   /// Return the size of the underlying storage.
858   std::size_t size() const noexcept; 858   std::size_t size() const noexcept;
859   859  
860   /// No-op resize. 860   /// No-op resize.
861   void resize(std::size_t) noexcept {} 861   void resize(std::size_t) noexcept {}
862   }; 862   };
863   863  
864   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF). 864   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
865   865  
866   Unlike the integer-based `multicast_interface_v6`, this option 866   Unlike the integer-based `multicast_interface_v6`, this option
867   takes an `ipv4_address` identifying the local interface. 867   takes an `ipv4_address` identifying the local interface.
868   868  
869   @par Example 869   @par Example
870   @code 870   @code
871   sock.set_option( socket_option::multicast_interface_v4( 871   sock.set_option( socket_option::multicast_interface_v4(
872   ipv4_address( "192.168.1.1" ) ) ); 872   ipv4_address( "192.168.1.1" ) ) );
873   @endcode 873   @endcode
874   */ 874   */
875   class BOOST_COROSIO_DECL multicast_interface_v4 875   class BOOST_COROSIO_DECL multicast_interface_v4
876   { 876   {
877   static constexpr std::size_t max_storage_ = 4; 877   static constexpr std::size_t max_storage_ = 4;
878   alignas(4) unsigned char storage_[max_storage_]{}; 878   alignas(4) unsigned char storage_[max_storage_]{};
879   879  
880   public: 880   public:
881   /// Construct with default values (INADDR_ANY). 881   /// Construct with default values (INADDR_ANY).
882   multicast_interface_v4() noexcept = default; 882   multicast_interface_v4() noexcept = default;
883   883  
884   /** Construct with an interface address. 884   /** Construct with an interface address.
885   885  
886   @param iface The local interface address. 886   @param iface The local interface address.
887   */ 887   */
888   explicit multicast_interface_v4(ipv4_address iface) noexcept; 888   explicit multicast_interface_v4(ipv4_address iface) noexcept;
889   889  
890   /// Return the protocol level. 890   /// Return the protocol level.
891   static int level() noexcept; 891   static int level() noexcept;
892   892  
893   /// Return the option name. 893   /// Return the option name.
894   static int name() noexcept; 894   static int name() noexcept;
895   895  
896   /// Return a pointer to the underlying storage. 896   /// Return a pointer to the underlying storage.
897   void* data() noexcept 897   void* data() noexcept
898   { 898   {
899   return storage_; 899   return storage_;
900   } 900   }
901   901  
902   /// Return a pointer to the underlying storage. 902   /// Return a pointer to the underlying storage.
HITCBC 903   2 void const* data() const noexcept 903   2 void const* data() const noexcept
904   { 904   {
HITCBC 905   2 return storage_; 905   2 return storage_;
906   } 906   }
907   907  
908   /// Return the size of the underlying storage. 908   /// Return the size of the underlying storage.
909   std::size_t size() const noexcept; 909   std::size_t size() const noexcept;
910   910  
911   /// No-op resize. 911   /// No-op resize.
912   void resize(std::size_t) noexcept {} 912   void resize(std::size_t) noexcept {}
913   }; 913   };
914   914  
915   } // namespace boost::corosio::socket_option 915   } // namespace boost::corosio::socket_option
916   916  
917   #endif // BOOST_COROSIO_SOCKET_OPTION_HPP 917   #endif // BOOST_COROSIO_SOCKET_OPTION_HPP