1 // 2 // prefer.hpp 3 // ~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #ifndef BOOST_ASIO_PREFER_HPP 12 #define BOOST_ASIO_PREFER_HPP 13 14 #if defined(_MSC_VER) && (_MSC_VER >= 1200) 15 # pragma once 16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 17 18 #include <boost/asio/detail/config.hpp> 19 #include <boost/asio/detail/type_traits.hpp> 20 #include <boost/asio/is_applicable_property.hpp> 21 #include <boost/asio/traits/prefer_free.hpp> 22 #include <boost/asio/traits/prefer_member.hpp> 23 #include <boost/asio/traits/require_free.hpp> 24 #include <boost/asio/traits/require_member.hpp> 25 #include <boost/asio/traits/static_require.hpp> 26 27 #include <boost/asio/detail/push_options.hpp> 28 29 #if defined(GENERATING_DOCUMENTATION) 30 31 namespace boost { 32 namespace asio { 33 34 /// A customisation point that attempts to apply a property to an object. 35 /** 36 * The name <tt>prefer</tt> denotes a customisation point object. The 37 * expression <tt>boost::asio::prefer(E, P0, Pn...)</tt> for some subexpressions 38 * <tt>E</tt> and <tt>P0</tt>, and where <tt>Pn...</tt> represents <tt>N</tt> 39 * subexpressions (where <tt>N</tt> is 0 or more, and with types <tt>T = 40 * decay_t<decltype(E)></tt> and <tt>Prop0 = decay_t<decltype(P0)></tt>) is 41 * expression-equivalent to: 42 * 43 * @li If <tt>is_applicable_property_v<T, Prop0> && Prop0::is_preferable</tt> is 44 * not a well-formed constant expression with value <tt>true</tt>, 45 * <tt>boost::asio::prefer(E, P0, Pn...)</tt> is ill-formed. 46 * 47 * @li Otherwise, <tt>E</tt> if <tt>N == 0</tt> and the expression 48 * <tt>Prop0::template static_query_v<T> == Prop0::value()</tt> is a 49 * well-formed constant expression with value <tt>true</tt>. 50 * 51 * @li Otherwise, <tt>(E).require(P0)</tt> if <tt>N == 0</tt> and the expression 52 * <tt>(E).require(P0)</tt> is a valid expression. 53 * 54 * @li Otherwise, <tt>require(E, P0)</tt> if <tt>N == 0</tt> and the expression 55 * <tt>require(E, P0)</tt> is a valid expression with overload resolution 56 * performed in a context that does not include the declaration of the 57 * <tt>require</tt> customization point object. 58 * 59 * @li Otherwise, <tt>(E).prefer(P0)</tt> if <tt>N == 0</tt> and the expression 60 * <tt>(E).prefer(P0)</tt> is a valid expression. 61 * 62 * @li Otherwise, <tt>prefer(E, P0)</tt> if <tt>N == 0</tt> and the expression 63 * <tt>prefer(E, P0)</tt> is a valid expression with overload resolution 64 * performed in a context that does not include the declaration of the 65 * <tt>prefer</tt> customization point object. 66 * 67 * @li Otherwise, <tt>E</tt> if <tt>N == 0</tt>. 68 * 69 * @li Otherwise, 70 * <tt>boost::asio::prefer(boost::asio::prefer(E, P0), Pn...)</tt> 71 * if <tt>N > 0</tt> and the expression 72 * <tt>boost::asio::prefer(boost::asio::prefer(E, P0), Pn...)</tt> 73 * is a valid expression. 74 * 75 * @li Otherwise, <tt>boost::asio::prefer(E, P0, Pn...)</tt> is ill-formed. 76 */ 77 inline constexpr unspecified prefer = unspecified; 78 79 /// A type trait that determines whether a @c prefer expression is well-formed. 80 /** 81 * Class template @c can_prefer is a trait that is derived from 82 * @c true_type if the expression <tt>boost::asio::prefer(std::declval<T>(), 83 * std::declval<Properties>()...)</tt> is well formed; otherwise @c false_type. 84 */ 85 template <typename T, typename... Properties> 86 struct can_prefer : 87 integral_constant<bool, automatically_determined> 88 { 89 }; 90 91 /// A type trait that determines whether a @c prefer expression will not throw. 92 /** 93 * Class template @c is_nothrow_prefer is a trait that is derived from 94 * @c true_type if the expression <tt>boost::asio::prefer(std::declval<T>(), 95 * std::declval<Properties>()...)</tt> is @c noexcept; otherwise @c false_type. 96 */ 97 template <typename T, typename... Properties> 98 struct is_nothrow_prefer : 99 integral_constant<bool, automatically_determined> 100 { 101 }; 102 103 /// A type trait that determines the result type of a @c prefer expression. 104 /** 105 * Class template @c prefer_result is a trait that determines the result 106 * type of the expression <tt>boost::asio::prefer(std::declval<T>(), 107 * std::declval<Properties>()...)</tt>. 108 */ 109 template <typename T, typename... Properties> 110 struct prefer_result 111 { 112 /// The result of the @c prefer expression. 113 typedef automatically_determined type; 114 }; 115 116 } // namespace asio 117 } // namespace boost 118 119 #else // defined(GENERATING_DOCUMENTATION) 120 121 namespace asio_prefer_fn { 122 123 using boost::asio::decay; 124 using boost::asio::declval; 125 using boost::asio::enable_if; 126 using boost::asio::is_applicable_property; 127 using boost::asio::traits::prefer_free; 128 using boost::asio::traits::prefer_member; 129 using boost::asio::traits::require_free; 130 using boost::asio::traits::require_member; 131 using boost::asio::traits::static_require; 132 133 void prefer(); 134 void require(); 135 136 enum overload_type 137 { 138 identity, 139 call_require_member, 140 call_require_free, 141 call_prefer_member, 142 call_prefer_free, 143 two_props, 144 n_props, 145 ill_formed 146 }; 147 148 template <typename T, typename Properties, typename = void> 149 struct call_traits 150 { 151 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = ill_formed); 152 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); 153 typedef void result_type; 154 }; 155 156 template <typename T, typename Property> 157 struct call_traits<T, void(Property), 158 typename enable_if< 159 ( 160 is_applicable_property< 161 typename decay<T>::type, 162 typename decay<Property>::type 163 >::value 164 && 165 decay<Property>::type::is_preferable 166 && 167 static_require<T, Property>::is_valid 168 ) 169 >::type> 170 { 171 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = identity); 172 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); 173 174 #if defined(BOOST_ASIO_HAS_MOVE) 175 typedef BOOST_ASIO_MOVE_ARG(T) result_type; 176 #else // defined(BOOST_ASIO_HAS_MOVE) 177 typedef BOOST_ASIO_MOVE_ARG(typename decay<T>::type) result_type; 178 #endif // defined(BOOST_ASIO_HAS_MOVE) 179 }; 180 181 template <typename T, typename Property> 182 struct call_traits<T, void(Property), 183 typename enable_if< 184 ( 185 is_applicable_property< 186 typename decay<T>::type, 187 typename decay<Property>::type 188 >::value 189 && 190 decay<Property>::type::is_preferable 191 && 192 !static_require<T, Property>::is_valid 193 && 194 require_member<T, Property>::is_valid 195 ) 196 >::type> : 197 require_member<T, Property> 198 { 199 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = call_require_member); 200 }; 201 202 template <typename T, typename Property> 203 struct call_traits<T, void(Property), 204 typename enable_if< 205 ( 206 is_applicable_property< 207 typename decay<T>::type, 208 typename decay<Property>::type 209 >::value 210 && 211 decay<Property>::type::is_preferable 212 && 213 !static_require<T, Property>::is_valid 214 && 215 !require_member<T, Property>::is_valid 216 && 217 require_free<T, Property>::is_valid 218 ) 219 >::type> : 220 require_free<T, Property> 221 { 222 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = call_require_free); 223 }; 224 225 template <typename T, typename Property> 226 struct call_traits<T, void(Property), 227 typename enable_if< 228 ( 229 is_applicable_property< 230 typename decay<T>::type, 231 typename decay<Property>::type 232 >::value 233 && 234 decay<Property>::type::is_preferable 235 && 236 !static_require<T, Property>::is_valid 237 && 238 !require_member<T, Property>::is_valid 239 && 240 !require_free<T, Property>::is_valid 241 && 242 prefer_member<T, Property>::is_valid 243 ) 244 >::type> : 245 prefer_member<T, Property> 246 { 247 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = call_prefer_member); 248 }; 249 250 template <typename T, typename Property> 251 struct call_traits<T, void(Property), 252 typename enable_if< 253 ( 254 is_applicable_property< 255 typename decay<T>::type, 256 typename decay<Property>::type 257 >::value 258 && 259 decay<Property>::type::is_preferable 260 && 261 !static_require<T, Property>::is_valid 262 && 263 !require_member<T, Property>::is_valid 264 && 265 !require_free<T, Property>::is_valid 266 && 267 !prefer_member<T, Property>::is_valid 268 && 269 prefer_free<T, Property>::is_valid 270 ) 271 >::type> : 272 prefer_free<T, Property> 273 { 274 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = call_prefer_free); 275 }; 276 277 template <typename T, typename Property> 278 struct call_traits<T, void(Property), 279 typename enable_if< 280 ( 281 is_applicable_property< 282 typename decay<T>::type, 283 typename decay<Property>::type 284 >::value 285 && 286 decay<Property>::type::is_preferable 287 && 288 !static_require<T, Property>::is_valid 289 && 290 !require_member<T, Property>::is_valid 291 && 292 !require_free<T, Property>::is_valid 293 && 294 !prefer_member<T, Property>::is_valid 295 && 296 !prefer_free<T, Property>::is_valid 297 ) 298 >::type> 299 { 300 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = identity); 301 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true); 302 303 #if defined(BOOST_ASIO_HAS_MOVE) 304 typedef BOOST_ASIO_MOVE_ARG(T) result_type; 305 #else // defined(BOOST_ASIO_HAS_MOVE) 306 typedef BOOST_ASIO_MOVE_ARG(typename decay<T>::type) result_type; 307 #endif // defined(BOOST_ASIO_HAS_MOVE) 308 }; 309 310 template <typename T, typename P0, typename P1> 311 struct call_traits<T, void(P0, P1), 312 typename enable_if< 313 call_traits<T, void(P0)>::overload != ill_formed 314 && 315 call_traits< 316 typename call_traits<T, void(P0)>::result_type, 317 void(P1) 318 >::overload != ill_formed 319 >::type> 320 { 321 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = two_props); 322 323 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = 324 ( 325 call_traits<T, void(P0)>::is_noexcept 326 && 327 call_traits< 328 typename call_traits<T, void(P0)>::result_type, 329 void(P1) 330 >::is_noexcept 331 )); 332 333 typedef typename decay< 334 typename call_traits< 335 typename call_traits<T, void(P0)>::result_type, 336 void(P1) 337 >::result_type 338 >::type result_type; 339 }; 340 341 template <typename T, typename P0, typename P1, typename BOOST_ASIO_ELLIPSIS PN> 342 struct call_traits<T, void(P0, P1, PN BOOST_ASIO_ELLIPSIS), 343 typename enable_if< 344 call_traits<T, void(P0)>::overload != ill_formed 345 && 346 call_traits< 347 typename call_traits<T, void(P0)>::result_type, 348 void(P1, PN BOOST_ASIO_ELLIPSIS) 349 >::overload != ill_formed 350 >::type> 351 { 352 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = n_props); 353 354 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = 355 ( 356 call_traits<T, void(P0)>::is_noexcept 357 && 358 call_traits< 359 typename call_traits<T, void(P0)>::result_type, 360 void(P1, PN BOOST_ASIO_ELLIPSIS) 361 >::is_noexcept 362 )); 363 364 typedef typename decay< 365 typename call_traits< 366 typename call_traits<T, void(P0)>::result_type, 367 void(P1, PN BOOST_ASIO_ELLIPSIS) 368 >::result_type 369 >::type result_type; 370 }; 371 372 struct impl 373 { 374 template <typename T, typename Property> 375 BOOST_ASIO_NODISCARD BOOST_ASIO_CONSTEXPR typename enable_if< 376 call_traits<T, void(Property)>::overload == identity, 377 typename call_traits<T, void(Property)>::result_type 378 >::type operator ()asio_prefer_fn::impl379 operator()( 380 BOOST_ASIO_MOVE_ARG(T) t, 381 BOOST_ASIO_MOVE_ARG(Property)) const 382 BOOST_ASIO_NOEXCEPT_IF(( 383 call_traits<T, void(Property)>::is_noexcept)) 384 { 385 return BOOST_ASIO_MOVE_CAST(T)(t); 386 } 387 388 template <typename T, typename Property> 389 BOOST_ASIO_NODISCARD BOOST_ASIO_CONSTEXPR typename enable_if< 390 call_traits<T, void(Property)>::overload == call_require_member, 391 typename call_traits<T, void(Property)>::result_type 392 >::type operator ()asio_prefer_fn::impl393 operator()( 394 BOOST_ASIO_MOVE_ARG(T) t, 395 BOOST_ASIO_MOVE_ARG(Property) p) const 396 BOOST_ASIO_NOEXCEPT_IF(( 397 call_traits<T, void(Property)>::is_noexcept)) 398 { 399 return BOOST_ASIO_MOVE_CAST(T)(t).require( 400 BOOST_ASIO_MOVE_CAST(Property)(p)); 401 } 402 403 template <typename T, typename Property> 404 BOOST_ASIO_NODISCARD BOOST_ASIO_CONSTEXPR typename enable_if< 405 call_traits<T, void(Property)>::overload == call_require_free, 406 typename call_traits<T, void(Property)>::result_type 407 >::type operator ()asio_prefer_fn::impl408 operator()( 409 BOOST_ASIO_MOVE_ARG(T) t, 410 BOOST_ASIO_MOVE_ARG(Property) p) const 411 BOOST_ASIO_NOEXCEPT_IF(( 412 call_traits<T, void(Property)>::is_noexcept)) 413 { 414 return require( 415 BOOST_ASIO_MOVE_CAST(T)(t), 416 BOOST_ASIO_MOVE_CAST(Property)(p)); 417 } 418 419 template <typename T, typename Property> 420 BOOST_ASIO_NODISCARD BOOST_ASIO_CONSTEXPR typename enable_if< 421 call_traits<T, void(Property)>::overload == call_prefer_member, 422 typename call_traits<T, void(Property)>::result_type 423 >::type operator ()asio_prefer_fn::impl424 operator()( 425 BOOST_ASIO_MOVE_ARG(T) t, 426 BOOST_ASIO_MOVE_ARG(Property) p) const 427 BOOST_ASIO_NOEXCEPT_IF(( 428 call_traits<T, void(Property)>::is_noexcept)) 429 { 430 return BOOST_ASIO_MOVE_CAST(T)(t).prefer( 431 BOOST_ASIO_MOVE_CAST(Property)(p)); 432 } 433 434 template <typename T, typename Property> 435 BOOST_ASIO_NODISCARD BOOST_ASIO_CONSTEXPR typename enable_if< 436 call_traits<T, void(Property)>::overload == call_prefer_free, 437 typename call_traits<T, void(Property)>::result_type 438 >::type operator ()asio_prefer_fn::impl439 operator()( 440 BOOST_ASIO_MOVE_ARG(T) t, 441 BOOST_ASIO_MOVE_ARG(Property) p) const 442 BOOST_ASIO_NOEXCEPT_IF(( 443 call_traits<T, void(Property)>::is_noexcept)) 444 { 445 return prefer( 446 BOOST_ASIO_MOVE_CAST(T)(t), 447 BOOST_ASIO_MOVE_CAST(Property)(p)); 448 } 449 450 template <typename T, typename P0, typename P1> 451 BOOST_ASIO_NODISCARD BOOST_ASIO_CONSTEXPR typename enable_if< 452 call_traits<T, void(P0, P1)>::overload == two_props, 453 typename call_traits<T, void(P0, P1)>::result_type 454 >::type operator ()asio_prefer_fn::impl455 operator()( 456 BOOST_ASIO_MOVE_ARG(T) t, 457 BOOST_ASIO_MOVE_ARG(P0) p0, 458 BOOST_ASIO_MOVE_ARG(P1) p1) const 459 BOOST_ASIO_NOEXCEPT_IF(( 460 call_traits<T, void(P0, P1)>::is_noexcept)) 461 { 462 return (*this)( 463 (*this)( 464 BOOST_ASIO_MOVE_CAST(T)(t), 465 BOOST_ASIO_MOVE_CAST(P0)(p0)), 466 BOOST_ASIO_MOVE_CAST(P1)(p1)); 467 } 468 469 template <typename T, typename P0, typename P1, 470 typename BOOST_ASIO_ELLIPSIS PN> 471 BOOST_ASIO_NODISCARD BOOST_ASIO_CONSTEXPR typename enable_if< 472 call_traits<T, void(P0, P1, PN BOOST_ASIO_ELLIPSIS)>::overload == n_props, 473 typename call_traits<T, void(P0, P1, PN BOOST_ASIO_ELLIPSIS)>::result_type 474 >::type operator ()asio_prefer_fn::impl475 operator()( 476 BOOST_ASIO_MOVE_ARG(T) t, 477 BOOST_ASIO_MOVE_ARG(P0) p0, 478 BOOST_ASIO_MOVE_ARG(P1) p1, 479 BOOST_ASIO_MOVE_ARG(PN) BOOST_ASIO_ELLIPSIS pn) const 480 BOOST_ASIO_NOEXCEPT_IF(( 481 call_traits<T, void(P0, P1, PN BOOST_ASIO_ELLIPSIS)>::is_noexcept)) 482 { 483 return (*this)( 484 (*this)( 485 BOOST_ASIO_MOVE_CAST(T)(t), 486 BOOST_ASIO_MOVE_CAST(P0)(p0)), 487 BOOST_ASIO_MOVE_CAST(P1)(p1), 488 BOOST_ASIO_MOVE_CAST(PN)(pn) BOOST_ASIO_ELLIPSIS); 489 } 490 }; 491 492 template <typename T = impl> 493 struct static_instance 494 { 495 static const T instance; 496 }; 497 498 template <typename T> 499 const T static_instance<T>::instance = {}; 500 501 } // namespace asio_prefer_fn 502 namespace boost { 503 namespace asio { 504 namespace { 505 506 static BOOST_ASIO_CONSTEXPR const asio_prefer_fn::impl& 507 prefer = asio_prefer_fn::static_instance<>::instance; 508 509 } // namespace 510 511 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 512 513 template <typename T, typename... Properties> 514 struct can_prefer : 515 integral_constant<bool, 516 asio_prefer_fn::call_traits<T, void(Properties...)>::overload 517 != asio_prefer_fn::ill_formed> 518 { 519 }; 520 521 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 522 523 template <typename T, typename P0 = void, 524 typename P1 = void, typename P2 = void> 525 struct can_prefer : 526 integral_constant<bool, 527 asio_prefer_fn::call_traits<T, void(P0, P1, P2)>::overload 528 != asio_prefer_fn::ill_formed> 529 { 530 }; 531 532 template <typename T, typename P0, typename P1> 533 struct can_prefer<T, P0, P1> : 534 integral_constant<bool, 535 asio_prefer_fn::call_traits<T, void(P0, P1)>::overload 536 != asio_prefer_fn::ill_formed> 537 { 538 }; 539 540 template <typename T, typename P0> 541 struct can_prefer<T, P0> : 542 integral_constant<bool, 543 asio_prefer_fn::call_traits<T, void(P0)>::overload 544 != asio_prefer_fn::ill_formed> 545 { 546 }; 547 548 template <typename T> 549 struct can_prefer<T> : 550 false_type 551 { 552 }; 553 554 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 555 556 #if defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 557 558 template <typename T, typename BOOST_ASIO_ELLIPSIS Properties> 559 constexpr bool can_prefer_v 560 = can_prefer<T, Properties BOOST_ASIO_ELLIPSIS>::value; 561 562 #endif // defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 563 564 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 565 566 template <typename T, typename... Properties> 567 struct is_nothrow_prefer : 568 integral_constant<bool, 569 asio_prefer_fn::call_traits<T, void(Properties...)>::is_noexcept> 570 { 571 }; 572 573 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 574 575 template <typename T, typename P0 = void, 576 typename P1 = void, typename P2 = void> 577 struct is_nothrow_prefer : 578 integral_constant<bool, 579 asio_prefer_fn::call_traits<T, void(P0, P1, P2)>::is_noexcept> 580 { 581 }; 582 583 template <typename T, typename P0, typename P1> 584 struct is_nothrow_prefer<T, P0, P1> : 585 integral_constant<bool, 586 asio_prefer_fn::call_traits<T, void(P0, P1)>::is_noexcept> 587 { 588 }; 589 590 template <typename T, typename P0> 591 struct is_nothrow_prefer<T, P0> : 592 integral_constant<bool, 593 asio_prefer_fn::call_traits<T, void(P0)>::is_noexcept> 594 { 595 }; 596 597 template <typename T> 598 struct is_nothrow_prefer<T> : 599 false_type 600 { 601 }; 602 603 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 604 605 #if defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 606 607 template <typename T, typename BOOST_ASIO_ELLIPSIS Properties> 608 constexpr bool is_nothrow_prefer_v 609 = is_nothrow_prefer<T, Properties BOOST_ASIO_ELLIPSIS>::value; 610 611 #endif // defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 612 613 #if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 614 615 template <typename T, typename... Properties> 616 struct prefer_result 617 { 618 typedef typename asio_prefer_fn::call_traits< 619 T, void(Properties...)>::result_type type; 620 }; 621 622 #else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 623 624 template <typename T, typename P0 = void, 625 typename P1 = void, typename P2 = void> 626 struct prefer_result 627 { 628 typedef typename asio_prefer_fn::call_traits< 629 T, void(P0, P1, P2)>::result_type type; 630 }; 631 632 template <typename T, typename P0, typename P1> 633 struct prefer_result<T, P0, P1> 634 { 635 typedef typename asio_prefer_fn::call_traits< 636 T, void(P0, P1)>::result_type type; 637 }; 638 639 template <typename T, typename P0> 640 struct prefer_result<T, P0> 641 { 642 typedef typename asio_prefer_fn::call_traits< 643 T, void(P0)>::result_type type; 644 }; 645 646 template <typename T> 647 struct prefer_result<T> 648 { 649 }; 650 651 #endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES) 652 653 } // namespace asio 654 } // namespace boost 655 656 #endif // defined(GENERATING_DOCUMENTATION) 657 658 #include <boost/asio/detail/pop_options.hpp> 659 660 #endif // BOOST_ASIO_PREFER_HPP 661