| 1 | // |
| 2 | // bind_allocator.hpp |
| 3 | // ~~~~~~~~~~~~~~~~~~ |
| 4 | // |
| 5 | // Copyright (c) 2003-2024 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_BIND_ALLOCATOR_HPP |
| 12 | #define BOOST_ASIO_BIND_ALLOCATOR_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/associated_allocator.hpp> |
| 21 | #include <boost/asio/associator.hpp> |
| 22 | #include <boost/asio/async_result.hpp> |
| 23 | |
| 24 | #include <boost/asio/detail/push_options.hpp> |
| 25 | |
| 26 | namespace boost { |
| 27 | namespace asio { |
| 28 | namespace detail { |
| 29 | |
| 30 | // Helper to automatically define nested typedef result_type. |
| 31 | |
| 32 | template <typename T, typename = void> |
| 33 | struct allocator_binder_result_type |
| 34 | { |
| 35 | protected: |
| 36 | typedef void result_type_or_void; |
| 37 | }; |
| 38 | |
| 39 | template <typename T> |
| 40 | struct allocator_binder_result_type<T, void_t<typename T::result_type>> |
| 41 | { |
| 42 | typedef typename T::result_type result_type; |
| 43 | protected: |
| 44 | typedef result_type result_type_or_void; |
| 45 | }; |
| 46 | |
| 47 | template <typename R> |
| 48 | struct allocator_binder_result_type<R(*)()> |
| 49 | { |
| 50 | typedef R result_type; |
| 51 | protected: |
| 52 | typedef result_type result_type_or_void; |
| 53 | }; |
| 54 | |
| 55 | template <typename R> |
| 56 | struct allocator_binder_result_type<R(&)()> |
| 57 | { |
| 58 | typedef R result_type; |
| 59 | protected: |
| 60 | typedef result_type result_type_or_void; |
| 61 | }; |
| 62 | |
| 63 | template <typename R, typename A1> |
| 64 | struct allocator_binder_result_type<R(*)(A1)> |
| 65 | { |
| 66 | typedef R result_type; |
| 67 | protected: |
| 68 | typedef result_type result_type_or_void; |
| 69 | }; |
| 70 | |
| 71 | template <typename R, typename A1> |
| 72 | struct allocator_binder_result_type<R(&)(A1)> |
| 73 | { |
| 74 | typedef R result_type; |
| 75 | protected: |
| 76 | typedef result_type result_type_or_void; |
| 77 | }; |
| 78 | |
| 79 | template <typename R, typename A1, typename A2> |
| 80 | struct allocator_binder_result_type<R(*)(A1, A2)> |
| 81 | { |
| 82 | typedef R result_type; |
| 83 | protected: |
| 84 | typedef result_type result_type_or_void; |
| 85 | }; |
| 86 | |
| 87 | template <typename R, typename A1, typename A2> |
| 88 | struct allocator_binder_result_type<R(&)(A1, A2)> |
| 89 | { |
| 90 | typedef R result_type; |
| 91 | protected: |
| 92 | typedef result_type result_type_or_void; |
| 93 | }; |
| 94 | |
| 95 | // Helper to automatically define nested typedef argument_type. |
| 96 | |
| 97 | template <typename T, typename = void> |
| 98 | struct allocator_binder_argument_type {}; |
| 99 | |
| 100 | template <typename T> |
| 101 | struct allocator_binder_argument_type<T, void_t<typename T::argument_type>> |
| 102 | { |
| 103 | typedef typename T::argument_type argument_type; |
| 104 | }; |
| 105 | |
| 106 | template <typename R, typename A1> |
| 107 | struct allocator_binder_argument_type<R(*)(A1)> |
| 108 | { |
| 109 | typedef A1 argument_type; |
| 110 | }; |
| 111 | |
| 112 | template <typename R, typename A1> |
| 113 | struct allocator_binder_argument_type<R(&)(A1)> |
| 114 | { |
| 115 | typedef A1 argument_type; |
| 116 | }; |
| 117 | |
| 118 | // Helper to automatically define nested typedefs first_argument_type and |
| 119 | // second_argument_type. |
| 120 | |
| 121 | template <typename T, typename = void> |
| 122 | struct allocator_binder_argument_types {}; |
| 123 | |
| 124 | template <typename T> |
| 125 | struct allocator_binder_argument_types<T, |
| 126 | void_t<typename T::first_argument_type>> |
| 127 | { |
| 128 | typedef typename T::first_argument_type first_argument_type; |
| 129 | typedef typename T::second_argument_type second_argument_type; |
| 130 | }; |
| 131 | |
| 132 | template <typename R, typename A1, typename A2> |
| 133 | struct allocator_binder_argument_type<R(*)(A1, A2)> |
| 134 | { |
| 135 | typedef A1 first_argument_type; |
| 136 | typedef A2 second_argument_type; |
| 137 | }; |
| 138 | |
| 139 | template <typename R, typename A1, typename A2> |
| 140 | struct allocator_binder_argument_type<R(&)(A1, A2)> |
| 141 | { |
| 142 | typedef A1 first_argument_type; |
| 143 | typedef A2 second_argument_type; |
| 144 | }; |
| 145 | |
| 146 | } // namespace detail |
| 147 | |
| 148 | /// A call wrapper type to bind an allocator of type @c Allocator |
| 149 | /// to an object of type @c T. |
| 150 | template <typename T, typename Allocator> |
| 151 | class allocator_binder |
| 152 | #if !defined(GENERATING_DOCUMENTATION) |
| 153 | : public detail::allocator_binder_result_type<T>, |
| 154 | public detail::allocator_binder_argument_type<T>, |
| 155 | public detail::allocator_binder_argument_types<T> |
| 156 | #endif // !defined(GENERATING_DOCUMENTATION) |
| 157 | { |
| 158 | public: |
| 159 | /// The type of the target object. |
| 160 | typedef T target_type; |
| 161 | |
| 162 | /// The type of the associated allocator. |
| 163 | typedef Allocator allocator_type; |
| 164 | |
| 165 | #if defined(GENERATING_DOCUMENTATION) |
| 166 | /// The return type if a function. |
| 167 | /** |
| 168 | * The type of @c result_type is based on the type @c T of the wrapper's |
| 169 | * target object: |
| 170 | * |
| 171 | * @li if @c T is a pointer to function type, @c result_type is a synonym for |
| 172 | * the return type of @c T; |
| 173 | * |
| 174 | * @li if @c T is a class type with a member type @c result_type, then @c |
| 175 | * result_type is a synonym for @c T::result_type; |
| 176 | * |
| 177 | * @li otherwise @c result_type is not defined. |
| 178 | */ |
| 179 | typedef see_below result_type; |
| 180 | |
| 181 | /// The type of the function's argument. |
| 182 | /** |
| 183 | * The type of @c argument_type is based on the type @c T of the wrapper's |
| 184 | * target object: |
| 185 | * |
| 186 | * @li if @c T is a pointer to a function type accepting a single argument, |
| 187 | * @c argument_type is a synonym for the return type of @c T; |
| 188 | * |
| 189 | * @li if @c T is a class type with a member type @c argument_type, then @c |
| 190 | * argument_type is a synonym for @c T::argument_type; |
| 191 | * |
| 192 | * @li otherwise @c argument_type is not defined. |
| 193 | */ |
| 194 | typedef see_below argument_type; |
| 195 | |
| 196 | /// The type of the function's first argument. |
| 197 | /** |
| 198 | * The type of @c first_argument_type is based on the type @c T of the |
| 199 | * wrapper's target object: |
| 200 | * |
| 201 | * @li if @c T is a pointer to a function type accepting two arguments, @c |
| 202 | * first_argument_type is a synonym for the return type of @c T; |
| 203 | * |
| 204 | * @li if @c T is a class type with a member type @c first_argument_type, |
| 205 | * then @c first_argument_type is a synonym for @c T::first_argument_type; |
| 206 | * |
| 207 | * @li otherwise @c first_argument_type is not defined. |
| 208 | */ |
| 209 | typedef see_below first_argument_type; |
| 210 | |
| 211 | /// The type of the function's second argument. |
| 212 | /** |
| 213 | * The type of @c second_argument_type is based on the type @c T of the |
| 214 | * wrapper's target object: |
| 215 | * |
| 216 | * @li if @c T is a pointer to a function type accepting two arguments, @c |
| 217 | * second_argument_type is a synonym for the return type of @c T; |
| 218 | * |
| 219 | * @li if @c T is a class type with a member type @c first_argument_type, |
| 220 | * then @c second_argument_type is a synonym for @c T::second_argument_type; |
| 221 | * |
| 222 | * @li otherwise @c second_argument_type is not defined. |
| 223 | */ |
| 224 | typedef see_below second_argument_type; |
| 225 | #endif // defined(GENERATING_DOCUMENTATION) |
| 226 | |
| 227 | /// Construct an allocator wrapper for the specified object. |
| 228 | /** |
| 229 | * This constructor is only valid if the type @c T is constructible from type |
| 230 | * @c U. |
| 231 | */ |
| 232 | template <typename U> |
| 233 | allocator_binder(const allocator_type& s, U&& u) |
| 234 | : allocator_(s), |
| 235 | target_(static_cast<U&&>(u)) |
| 236 | { |
| 237 | } |
| 238 | |
| 239 | /// Copy constructor. |
| 240 | allocator_binder(const allocator_binder& other) |
| 241 | : allocator_(other.get_allocator()), |
| 242 | target_(other.get()) |
| 243 | { |
| 244 | } |
| 245 | |
| 246 | /// Construct a copy, but specify a different allocator. |
| 247 | allocator_binder(const allocator_type& s, const allocator_binder& other) |
| 248 | : allocator_(s), |
| 249 | target_(other.get()) |
| 250 | { |
| 251 | } |
| 252 | |
| 253 | /// Construct a copy of a different allocator wrapper type. |
| 254 | /** |
| 255 | * This constructor is only valid if the @c Allocator type is |
| 256 | * constructible from type @c OtherAllocator, and the type @c T is |
| 257 | * constructible from type @c U. |
| 258 | */ |
| 259 | template <typename U, typename OtherAllocator> |
| 260 | allocator_binder(const allocator_binder<U, OtherAllocator>& other, |
| 261 | constraint_t<is_constructible<Allocator, OtherAllocator>::value> = 0, |
| 262 | constraint_t<is_constructible<T, U>::value> = 0) |
| 263 | : allocator_(other.get_allocator()), |
| 264 | target_(other.get()) |
| 265 | { |
| 266 | } |
| 267 | |
| 268 | /// Construct a copy of a different allocator wrapper type, but |
| 269 | /// specify a different allocator. |
| 270 | /** |
| 271 | * This constructor is only valid if the type @c T is constructible from type |
| 272 | * @c U. |
| 273 | */ |
| 274 | template <typename U, typename OtherAllocator> |
| 275 | allocator_binder(const allocator_type& s, |
| 276 | const allocator_binder<U, OtherAllocator>& other, |
| 277 | constraint_t<is_constructible<T, U>::value> = 0) |
| 278 | : allocator_(s), |
| 279 | target_(other.get()) |
| 280 | { |
| 281 | } |
| 282 | |
| 283 | /// Move constructor. |
| 284 | allocator_binder(allocator_binder&& other) |
| 285 | : allocator_(static_cast<allocator_type&&>( |
| 286 | other.get_allocator())), |
| 287 | target_(static_cast<T&&>(other.get())) |
| 288 | { |
| 289 | } |
| 290 | |
| 291 | /// Move construct the target object, but specify a different allocator. |
| 292 | allocator_binder(const allocator_type& s, |
| 293 | allocator_binder&& other) |
| 294 | : allocator_(s), |
| 295 | target_(static_cast<T&&>(other.get())) |
| 296 | { |
| 297 | } |
| 298 | |
| 299 | /// Move construct from a different allocator wrapper type. |
| 300 | template <typename U, typename OtherAllocator> |
| 301 | allocator_binder( |
| 302 | allocator_binder<U, OtherAllocator>&& other, |
| 303 | constraint_t<is_constructible<Allocator, OtherAllocator>::value> = 0, |
| 304 | constraint_t<is_constructible<T, U>::value> = 0) |
| 305 | : allocator_(static_cast<OtherAllocator&&>( |
| 306 | other.get_allocator())), |
| 307 | target_(static_cast<U&&>(other.get())) |
| 308 | { |
| 309 | } |
| 310 | |
| 311 | /// Move construct from a different allocator wrapper type, but |
| 312 | /// specify a different allocator. |
| 313 | template <typename U, typename OtherAllocator> |
| 314 | allocator_binder(const allocator_type& s, |
| 315 | allocator_binder<U, OtherAllocator>&& other, |
| 316 | constraint_t<is_constructible<T, U>::value> = 0) |
| 317 | : allocator_(s), |
| 318 | target_(static_cast<U&&>(other.get())) |
| 319 | { |
| 320 | } |
| 321 | |
| 322 | /// Destructor. |
| 323 | ~allocator_binder() |
| 324 | { |
| 325 | } |
| 326 | |
| 327 | /// Obtain a reference to the target object. |
| 328 | target_type& get() noexcept |
| 329 | { |
| 330 | return target_; |
| 331 | } |
| 332 | |
| 333 | /// Obtain a reference to the target object. |
| 334 | const target_type& get() const noexcept |
| 335 | { |
| 336 | return target_; |
| 337 | } |
| 338 | |
| 339 | /// Obtain the associated allocator. |
| 340 | allocator_type get_allocator() const noexcept |
| 341 | { |
| 342 | return allocator_; |
| 343 | } |
| 344 | |
| 345 | /// Forwarding function call operator. |
| 346 | template <typename... Args> |
| 347 | result_of_t<T(Args...)> operator()(Args&&... args) |
| 348 | { |
| 349 | return target_(static_cast<Args&&>(args)...); |
| 350 | } |
| 351 | |
| 352 | /// Forwarding function call operator. |
| 353 | template <typename... Args> |
| 354 | result_of_t<T(Args...)> operator()(Args&&... args) const |
| 355 | { |
| 356 | return target_(static_cast<Args&&>(args)...); |
| 357 | } |
| 358 | |
| 359 | private: |
| 360 | Allocator allocator_; |
| 361 | T target_; |
| 362 | }; |
| 363 | |
| 364 | /// Associate an object of type @c T with an allocator of type |
| 365 | /// @c Allocator. |
| 366 | template <typename Allocator, typename T> |
| 367 | BOOST_ASIO_NODISCARD inline allocator_binder<decay_t<T>, Allocator> |
| 368 | bind_allocator(const Allocator& s, T&& t) |
| 369 | { |
| 370 | return allocator_binder<decay_t<T>, Allocator>(s, static_cast<T&&>(t)); |
| 371 | } |
| 372 | |
| 373 | #if !defined(GENERATING_DOCUMENTATION) |
| 374 | |
| 375 | namespace detail { |
| 376 | |
| 377 | template <typename TargetAsyncResult, typename Allocator, typename = void> |
| 378 | class allocator_binder_completion_handler_async_result |
| 379 | { |
| 380 | public: |
| 381 | template <typename T> |
| 382 | explicit allocator_binder_completion_handler_async_result(T&) |
| 383 | { |
| 384 | } |
| 385 | }; |
| 386 | |
| 387 | template <typename TargetAsyncResult, typename Allocator> |
| 388 | class allocator_binder_completion_handler_async_result< |
| 389 | TargetAsyncResult, Allocator, |
| 390 | void_t<typename TargetAsyncResult::completion_handler_type>> |
| 391 | { |
| 392 | private: |
| 393 | TargetAsyncResult target_; |
| 394 | |
| 395 | public: |
| 396 | typedef allocator_binder< |
| 397 | typename TargetAsyncResult::completion_handler_type, Allocator> |
| 398 | completion_handler_type; |
| 399 | |
| 400 | explicit allocator_binder_completion_handler_async_result( |
| 401 | typename TargetAsyncResult::completion_handler_type& handler) |
| 402 | : target_(handler) |
| 403 | { |
| 404 | } |
| 405 | |
| 406 | auto get() -> decltype(target_.get()) |
| 407 | { |
| 408 | return target_.get(); |
| 409 | } |
| 410 | }; |
| 411 | |
| 412 | template <typename TargetAsyncResult, typename = void> |
| 413 | struct allocator_binder_async_result_return_type |
| 414 | { |
| 415 | }; |
| 416 | |
| 417 | template <typename TargetAsyncResult> |
| 418 | struct allocator_binder_async_result_return_type< |
| 419 | TargetAsyncResult, void_type<typename TargetAsyncResult::return_type>> |
| 420 | { |
| 421 | typedef typename TargetAsyncResult::return_type return_type; |
| 422 | }; |
| 423 | |
| 424 | } // namespace detail |
| 425 | |
| 426 | template <typename T, typename Allocator, typename Signature> |
| 427 | class async_result<allocator_binder<T, Allocator>, Signature> : |
| 428 | public detail::allocator_binder_completion_handler_async_result< |
| 429 | async_result<T, Signature>, Allocator>, |
| 430 | public detail::allocator_binder_async_result_return_type< |
| 431 | async_result<T, Signature>> |
| 432 | { |
| 433 | public: |
| 434 | explicit async_result(allocator_binder<T, Allocator>& b) |
| 435 | : detail::allocator_binder_completion_handler_async_result< |
| 436 | async_result<T, Signature>, Allocator>(b.get()) |
| 437 | { |
| 438 | } |
| 439 | |
| 440 | template <typename Initiation> |
| 441 | struct init_wrapper |
| 442 | { |
| 443 | template <typename Init> |
| 444 | init_wrapper(const Allocator& allocator, Init&& init) |
| 445 | : allocator_(allocator), |
| 446 | initiation_(static_cast<Init&&>(init)) |
| 447 | { |
| 448 | } |
| 449 | |
| 450 | template <typename Handler, typename... Args> |
| 451 | void operator()(Handler&& handler, Args&&... args) |
| 452 | { |
| 453 | static_cast<Initiation&&>(initiation_)( |
| 454 | allocator_binder<decay_t<Handler>, Allocator>( |
| 455 | allocator_, static_cast<Handler&&>(handler)), |
| 456 | static_cast<Args&&>(args)...); |
| 457 | } |
| 458 | |
| 459 | template <typename Handler, typename... Args> |
| 460 | void operator()(Handler&& handler, Args&&... args) const |
| 461 | { |
| 462 | initiation_( |
| 463 | allocator_binder<decay_t<Handler>, Allocator>( |
| 464 | allocator_, static_cast<Handler&&>(handler)), |
| 465 | static_cast<Args&&>(args)...); |
| 466 | } |
| 467 | |
| 468 | Allocator allocator_; |
| 469 | Initiation initiation_; |
| 470 | }; |
| 471 | |
| 472 | template <typename Initiation, typename RawCompletionToken, typename... Args> |
| 473 | static auto initiate(Initiation&& initiation, |
| 474 | RawCompletionToken&& token, Args&&... args) |
| 475 | -> decltype( |
| 476 | async_initiate<T, Signature>( |
| 477 | declval<init_wrapper<decay_t<Initiation>>>(), |
| 478 | token.get(), static_cast<Args&&>(args)...)) |
| 479 | { |
| 480 | return async_initiate<T, Signature>( |
| 481 | init_wrapper<decay_t<Initiation>>(token.get_allocator(), |
| 482 | static_cast<Initiation&&>(initiation)), |
| 483 | token.get(), static_cast<Args&&>(args)...); |
| 484 | } |
| 485 | |
| 486 | private: |
| 487 | async_result(const async_result&) = delete; |
| 488 | async_result& operator=(const async_result&) = delete; |
| 489 | |
| 490 | async_result<T, Signature> target_; |
| 491 | }; |
| 492 | |
| 493 | template <template <typename, typename> class Associator, |
| 494 | typename T, typename Allocator, typename DefaultCandidate> |
| 495 | struct associator<Associator, allocator_binder<T, Allocator>, DefaultCandidate> |
| 496 | : Associator<T, DefaultCandidate> |
| 497 | { |
| 498 | static typename Associator<T, DefaultCandidate>::type get( |
| 499 | const allocator_binder<T, Allocator>& b) noexcept |
| 500 | { |
| 501 | return Associator<T, DefaultCandidate>::get(b.get()); |
| 502 | } |
| 503 | |
| 504 | static auto get(const allocator_binder<T, Allocator>& b, |
| 505 | const DefaultCandidate& c) noexcept |
| 506 | -> decltype(Associator<T, DefaultCandidate>::get(b.get(), c)) |
| 507 | { |
| 508 | return Associator<T, DefaultCandidate>::get(b.get(), c); |
| 509 | } |
| 510 | }; |
| 511 | |
| 512 | template <typename T, typename Allocator, typename Allocator1> |
| 513 | struct associated_allocator<allocator_binder<T, Allocator>, Allocator1> |
| 514 | { |
| 515 | typedef Allocator type; |
| 516 | |
| 517 | static auto get(const allocator_binder<T, Allocator>& b, |
| 518 | const Allocator1& = Allocator1()) noexcept |
| 519 | -> decltype(b.get_allocator()) |
| 520 | { |
| 521 | return b.get_allocator(); |
| 522 | } |
| 523 | }; |
| 524 | |
| 525 | #endif // !defined(GENERATING_DOCUMENTATION) |
| 526 | |
| 527 | } // namespace asio |
| 528 | } // namespace boost |
| 529 | |
| 530 | #include <boost/asio/detail/pop_options.hpp> |
| 531 | |
| 532 | #endif // BOOST_ASIO_BIND_ALLOCATOR_HPP |
| 533 | |