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