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