diff --git a/doc/quickref.xml b/doc/quickref.xml
index c3a22cf6..3af3dad9 100644
--- a/doc/quickref.xml
+++ b/doc/quickref.xml
@@ -234,6 +234,7 @@
asio_handler_is_continuation
async_compose
async_initiate
+ bind_allocator
bind_cancellation_slot
bind_executor
co_spawn
diff --git a/example/cpp03/allocation/server.cpp b/example/cpp03/allocation/server.cpp
index ef447ce1..10cc6cfd 100644
--- a/example/cpp03/allocation/server.cpp
+++ b/example/cpp03/allocation/server.cpp
@@ -117,52 +117,6 @@ public:
handler_memory& memory_;
};
-// Wrapper class template for handler objects to allow handler memory
-// allocation to be customised. The allocator_type typedef and get_allocator()
-// member function are used by the asynchronous operations to obtain the
-// allocator. Calls to operator() are forwarded to the encapsulated handler.
-template
-class custom_alloc_handler
-{
-public:
- typedef handler_allocator allocator_type;
-
- custom_alloc_handler(handler_memory& m, Handler h)
- : memory_(m),
- handler_(h)
- {
- }
-
- allocator_type get_allocator() const
- {
- return allocator_type(memory_);
- }
-
- template
- void operator()(Arg1 arg1)
- {
- handler_(arg1);
- }
-
- template
- void operator()(Arg1 arg1, Arg2 arg2)
- {
- handler_(arg1, arg2);
- }
-
-private:
- handler_memory& memory_;
- Handler handler_;
-};
-
-// Helper function to wrap a handler object to add custom allocation.
-template
-inline custom_alloc_handler make_custom_alloc_handler(
- handler_memory& m, Handler h)
-{
- return custom_alloc_handler(m, h);
-}
-
class session
: public boost::enable_shared_from_this
{
@@ -180,7 +134,8 @@ public:
void start()
{
socket_.async_read_some(boost::asio::buffer(data_),
- make_custom_alloc_handler(handler_memory_,
+ boost::asio::bind_allocator(
+ handler_allocator(handler_memory_),
boost::bind(&session::handle_read,
shared_from_this(),
boost::asio::placeholders::error,
@@ -194,7 +149,8 @@ public:
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
- make_custom_alloc_handler(handler_memory_,
+ boost::asio::bind_allocator(
+ handler_allocator(handler_memory_),
boost::bind(&session::handle_write,
shared_from_this(),
boost::asio::placeholders::error)));
@@ -206,7 +162,8 @@ public:
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_),
- make_custom_alloc_handler(handler_memory_,
+ boost::asio::bind_allocator(
+ handler_allocator(handler_memory_),
boost::bind(&session::handle_read,
shared_from_this(),
boost::asio::placeholders::error,
diff --git a/example/cpp11/allocation/server.cpp b/example/cpp11/allocation/server.cpp
index 5998f169..eb46f84b 100644
--- a/example/cpp11/allocation/server.cpp
+++ b/example/cpp11/allocation/server.cpp
@@ -112,46 +112,6 @@ private:
handler_memory& memory_;
};
-// Wrapper class template for handler objects to allow handler memory
-// allocation to be customised. The allocator_type type and get_allocator()
-// member function are used by the asynchronous operations to obtain the
-// allocator. Calls to operator() are forwarded to the encapsulated handler.
-template
-class custom_alloc_handler
-{
-public:
- using allocator_type = handler_allocator;
-
- custom_alloc_handler(handler_memory& m, Handler h)
- : memory_(m),
- handler_(h)
- {
- }
-
- allocator_type get_allocator() const noexcept
- {
- return allocator_type(memory_);
- }
-
- template
- void operator()(Args&&... args)
- {
- handler_(std::forward(args)...);
- }
-
-private:
- handler_memory& memory_;
- Handler handler_;
-};
-
-// Helper function to wrap a handler object to add custom allocation.
-template
-inline custom_alloc_handler make_custom_alloc_handler(
- handler_memory& m, Handler h)
-{
- return custom_alloc_handler(m, h);
-}
-
class session
: public std::enable_shared_from_this
{
@@ -171,7 +131,8 @@ private:
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_),
- make_custom_alloc_handler(handler_memory_,
+ boost::asio::bind_allocator(
+ handler_allocator(handler_memory_),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
@@ -185,7 +146,8 @@ private:
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
- make_custom_alloc_handler(handler_memory_,
+ boost::asio::bind_allocator(
+ handler_allocator(handler_memory_),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
diff --git a/include/boost/asio.hpp b/include/boost/asio.hpp
index 116f0737..1659f4db 100644
--- a/include/boost/asio.hpp
+++ b/include/boost/asio.hpp
@@ -42,6 +42,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/boost/asio/bind_allocator.hpp b/include/boost/asio/bind_allocator.hpp
new file mode 100644
index 00000000..ef60acf5
--- /dev/null
+++ b/include/boost/asio/bind_allocator.hpp
@@ -0,0 +1,724 @@
+//
+// bind_allocator.hpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BOOST_ASIO_BIND_ALLOCATOR_HPP
+#define BOOST_ASIO_BIND_ALLOCATOR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace boost {
+namespace asio {
+namespace detail {
+
+// Helper to automatically define nested typedef result_type.
+
+template
+struct allocator_binder_result_type
+{
+protected:
+ typedef void result_type_or_void;
+};
+
+template
+struct allocator_binder_result_type::type>
+{
+ typedef typename T::result_type result_type;
+protected:
+ typedef result_type result_type_or_void;
+};
+
+template
+struct allocator_binder_result_type
+{
+ typedef R result_type;
+protected:
+ typedef result_type result_type_or_void;
+};
+
+template
+struct allocator_binder_result_type
+{
+ typedef R result_type;
+protected:
+ typedef result_type result_type_or_void;
+};
+
+template
+struct allocator_binder_result_type
+{
+ typedef R result_type;
+protected:
+ typedef result_type result_type_or_void;
+};
+
+template
+struct allocator_binder_result_type
+{
+ typedef R result_type;
+protected:
+ typedef result_type result_type_or_void;
+};
+
+template
+struct allocator_binder_result_type
+{
+ typedef R result_type;
+protected:
+ typedef result_type result_type_or_void;
+};
+
+template
+struct allocator_binder_result_type
+{
+ typedef R result_type;
+protected:
+ typedef result_type result_type_or_void;
+};
+
+// Helper to automatically define nested typedef argument_type.
+
+template
+struct allocator_binder_argument_type {};
+
+template
+struct allocator_binder_argument_type::type>
+{
+ typedef typename T::argument_type argument_type;
+};
+
+template
+struct allocator_binder_argument_type
+{
+ typedef A1 argument_type;
+};
+
+template
+struct allocator_binder_argument_type
+{
+ typedef A1 argument_type;
+};
+
+// Helper to automatically define nested typedefs first_argument_type and
+// second_argument_type.
+
+template
+struct allocator_binder_argument_types {};
+
+template
+struct allocator_binder_argument_types::type>
+{
+ typedef typename T::first_argument_type first_argument_type;
+ typedef typename T::second_argument_type second_argument_type;
+};
+
+template
+struct allocator_binder_argument_type
+{
+ typedef A1 first_argument_type;
+ typedef A2 second_argument_type;
+};
+
+template
+struct allocator_binder_argument_type
+{
+ typedef A1 first_argument_type;
+ typedef A2 second_argument_type;
+};
+
+// Helper to enable SFINAE on zero-argument operator() below.
+
+template
+struct allocator_binder_result_of0
+{
+ typedef void type;
+};
+
+template
+struct allocator_binder_result_of0::type>::type>
+{
+ typedef typename result_of::type type;
+};
+
+} // namespace detail
+
+/// A call wrapper type to bind an allocator of type @c Allocator
+/// to an object of type @c T.
+template
+class allocator_binder
+#if !defined(GENERATING_DOCUMENTATION)
+ : public detail::allocator_binder_result_type,
+ public detail::allocator_binder_argument_type,
+ public detail::allocator_binder_argument_types
+#endif // !defined(GENERATING_DOCUMENTATION)
+{
+public:
+ /// The type of the target object.
+ typedef T target_type;
+
+ /// The type of the associated allocator.
+ typedef Allocator allocator_type;
+
+#if defined(GENERATING_DOCUMENTATION)
+ /// The return type if a function.
+ /**
+ * The type of @c result_type is based on the type @c T of the wrapper's
+ * target object:
+ *
+ * @li if @c T is a pointer to function type, @c result_type is a synonym for
+ * the return type of @c T;
+ *
+ * @li if @c T is a class type with a member type @c result_type, then @c
+ * result_type is a synonym for @c T::result_type;
+ *
+ * @li otherwise @c result_type is not defined.
+ */
+ typedef see_below result_type;
+
+ /// The type of the function's argument.
+ /**
+ * The type of @c argument_type is based on the type @c T of the wrapper's
+ * target object:
+ *
+ * @li if @c T is a pointer to a function type accepting a single argument,
+ * @c argument_type is a synonym for the return type of @c T;
+ *
+ * @li if @c T is a class type with a member type @c argument_type, then @c
+ * argument_type is a synonym for @c T::argument_type;
+ *
+ * @li otherwise @c argument_type is not defined.
+ */
+ typedef see_below argument_type;
+
+ /// The type of the function's first argument.
+ /**
+ * The type of @c first_argument_type is based on the type @c T of the
+ * wrapper's target object:
+ *
+ * @li if @c T is a pointer to a function type accepting two arguments, @c
+ * first_argument_type is a synonym for the return type of @c T;
+ *
+ * @li if @c T is a class type with a member type @c first_argument_type,
+ * then @c first_argument_type is a synonym for @c T::first_argument_type;
+ *
+ * @li otherwise @c first_argument_type is not defined.
+ */
+ typedef see_below first_argument_type;
+
+ /// The type of the function's second argument.
+ /**
+ * The type of @c second_argument_type is based on the type @c T of the
+ * wrapper's target object:
+ *
+ * @li if @c T is a pointer to a function type accepting two arguments, @c
+ * second_argument_type is a synonym for the return type of @c T;
+ *
+ * @li if @c T is a class type with a member type @c first_argument_type,
+ * then @c second_argument_type is a synonym for @c T::second_argument_type;
+ *
+ * @li otherwise @c second_argument_type is not defined.
+ */
+ typedef see_below second_argument_type;
+#endif // defined(GENERATING_DOCUMENTATION)
+
+ /// Construct an allocator wrapper for the specified object.
+ /**
+ * This constructor is only valid if the type @c T is constructible from type
+ * @c U.
+ */
+ template
+ allocator_binder(const allocator_type& s,
+ BOOST_ASIO_MOVE_ARG(U) u)
+ : allocator_(s),
+ target_(BOOST_ASIO_MOVE_CAST(U)(u))
+ {
+ }
+
+ /// Copy constructor.
+ allocator_binder(const allocator_binder& other)
+ : allocator_(other.get_allocator()),
+ target_(other.get())
+ {
+ }
+
+ /// Construct a copy, but specify a different allocator.
+ allocator_binder(const allocator_type& s,
+ const allocator_binder& other)
+ : allocator_(s),
+ target_(other.get())
+ {
+ }
+
+ /// Construct a copy of a different allocator wrapper type.
+ /**
+ * This constructor is only valid if the @c Allocator type is
+ * constructible from type @c OtherAllocator, and the type @c T is
+ * constructible from type @c U.
+ */
+ template
+ allocator_binder(
+ const allocator_binder& other)
+ : allocator_(other.get_allocator()),
+ target_(other.get())
+ {
+ }
+
+ /// Construct a copy of a different allocator wrapper type, but
+ /// specify a different allocator.
+ /**
+ * This constructor is only valid if the type @c T is constructible from type
+ * @c U.
+ */
+ template
+ allocator_binder(const allocator_type& s,
+ const allocator_binder& other)
+ : allocator_(s),
+ target_(other.get())
+ {
+ }
+
+#if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
+
+ /// Move constructor.
+ allocator_binder(allocator_binder&& other)
+ : allocator_(BOOST_ASIO_MOVE_CAST(allocator_type)(
+ other.get_allocator())),
+ target_(BOOST_ASIO_MOVE_CAST(T)(other.get()))
+ {
+ }
+
+ /// Move construct the target object, but specify a different allocator.
+ allocator_binder(const allocator_type& s,
+ allocator_binder&& other)
+ : allocator_(s),
+ target_(BOOST_ASIO_MOVE_CAST(T)(other.get()))
+ {
+ }
+
+ /// Move construct from a different allocator wrapper type.
+ template
+ allocator_binder(
+ allocator_binder&& other)
+ : allocator_(BOOST_ASIO_MOVE_CAST(OtherAllocator)(
+ other.get_allocator())),
+ target_(BOOST_ASIO_MOVE_CAST(U)(other.get()))
+ {
+ }
+
+ /// Move construct from a different allocator wrapper type, but
+ /// specify a different allocator.
+ template
+ allocator_binder(const allocator_type& s,
+ allocator_binder&& other)
+ : allocator_(s),
+ target_(BOOST_ASIO_MOVE_CAST(U)(other.get()))
+ {
+ }
+
+#endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
+
+ /// Destructor.
+ ~allocator_binder()
+ {
+ }
+
+ /// Obtain a reference to the target object.
+ target_type& get() BOOST_ASIO_NOEXCEPT
+ {
+ return target_;
+ }
+
+ /// Obtain a reference to the target object.
+ const target_type& get() const BOOST_ASIO_NOEXCEPT
+ {
+ return target_;
+ }
+
+ /// Obtain the associated allocator.
+ allocator_type get_allocator() const BOOST_ASIO_NOEXCEPT
+ {
+ return allocator_;
+ }
+
+#if defined(GENERATING_DOCUMENTATION)
+
+ template auto operator()(Args&& ...);
+ template auto operator()(Args&& ...) const;
+
+#elif defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ /// Forwarding function call operator.
+ template
+ typename result_of::type operator()(
+ BOOST_ASIO_MOVE_ARG(Args)... args)
+ {
+ return target_(BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+ /// Forwarding function call operator.
+ template
+ typename result_of::type operator()(
+ BOOST_ASIO_MOVE_ARG(Args)... args) const
+ {
+ return target_(BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+#elif defined(BOOST_ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
+
+ typename detail::allocator_binder_result_of0::type operator()()
+ {
+ return target_();
+ }
+
+ typename detail::allocator_binder_result_of0::type
+ operator()() const
+ {
+ return target_();
+ }
+
+#define BOOST_ASIO_PRIVATE_BINDER_CALL_DEF(n) \
+ template \
+ typename result_of::type operator()( \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ { \
+ return target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ \
+ template \
+ typename result_of::type operator()( \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) const \
+ { \
+ return target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_BINDER_CALL_DEF)
+#undef BOOST_ASIO_PRIVATE_BINDER_CALL_DEF
+
+#else // defined(BOOST_ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
+
+ typedef typename detail::allocator_binder_result_type<
+ T>::result_type_or_void result_type_or_void;
+
+ result_type_or_void operator()()
+ {
+ return target_();
+ }
+
+ result_type_or_void operator()() const
+ {
+ return target_();
+ }
+
+#define BOOST_ASIO_PRIVATE_BINDER_CALL_DEF(n) \
+ template \
+ result_type_or_void operator()( \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ { \
+ return target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ \
+ template \
+ result_type_or_void operator()( \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) const \
+ { \
+ return target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_BINDER_CALL_DEF)
+#undef BOOST_ASIO_PRIVATE_BINDER_CALL_DEF
+
+#endif // defined(BOOST_ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
+
+private:
+ Allocator allocator_;
+ T target_;
+};
+
+/// Associate an object of type @c T with an allocator of type
+/// @c Allocator.
+template
+inline allocator_binder::type, Allocator>
+bind_allocator(const Allocator& s, BOOST_ASIO_MOVE_ARG(T) t)
+{
+ return allocator_binder<
+ typename decay::type, Allocator>(
+ s, BOOST_ASIO_MOVE_CAST(T)(t));
+}
+
+#if !defined(GENERATING_DOCUMENTATION)
+
+namespace detail {
+
+template
+struct allocator_binder_async_result_completion_handler_type
+{
+};
+
+template
+struct allocator_binder_async_result_completion_handler_type<
+ TargetAsyncResult, Allocator,
+ typename void_type<
+ typename TargetAsyncResult::completion_handler_type
+ >::type>
+{
+ typedef allocator_binder<
+ typename TargetAsyncResult::completion_handler_type, Allocator>
+ completion_handler_type;
+};
+
+template
+struct allocator_binder_async_result_return_type
+{
+};
+
+template
+struct allocator_binder_async_result_return_type<
+ TargetAsyncResult,
+ typename void_type<
+ typename TargetAsyncResult::return_type
+ >::type>
+{
+ typedef typename TargetAsyncResult::return_type return_type;
+};
+
+} // namespace detail
+
+template
+class async_result, Signature> :
+ public detail::allocator_binder_async_result_completion_handler_type<
+ async_result, Allocator>,
+ public detail::allocator_binder_async_result_return_type<
+ async_result >
+{
+public:
+ explicit async_result(allocator_binder& b)
+ : target_(b.get())
+ {
+ }
+
+ typename async_result::return_type get()
+ {
+ return target_.get();
+ }
+
+ template
+ struct init_wrapper
+ {
+ template
+ init_wrapper(const Allocator& allocator, BOOST_ASIO_MOVE_ARG(Init) init)
+ : allocator_(allocator),
+ initiation_(BOOST_ASIO_MOVE_CAST(Init)(init))
+ {
+ }
+
+#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template
+ void operator()(
+ BOOST_ASIO_MOVE_ARG(Handler) handler,
+ BOOST_ASIO_MOVE_ARG(Args)... args)
+ {
+ BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)(
+ allocator_binder<
+ typename decay::type, Allocator>(
+ allocator_, BOOST_ASIO_MOVE_CAST(Handler)(handler)),
+ BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+ template
+ void operator()(
+ BOOST_ASIO_MOVE_ARG(Handler) handler,
+ BOOST_ASIO_MOVE_ARG(Args)... args) const
+ {
+ initiation_(
+ allocator_binder<
+ typename decay::type, Allocator>(
+ allocator_, BOOST_ASIO_MOVE_CAST(Handler)(handler)),
+ BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+#else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template
+ void operator()(
+ BOOST_ASIO_MOVE_ARG(Handler) handler)
+ {
+ BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)(
+ allocator_binder<
+ typename decay::type, Allocator>(
+ allocator_, BOOST_ASIO_MOVE_CAST(Handler)(handler)));
+ }
+
+ template
+ void operator()(
+ BOOST_ASIO_MOVE_ARG(Handler) handler) const
+ {
+ initiation_(
+ allocator_binder<
+ typename decay::type, Allocator>(
+ allocator_, BOOST_ASIO_MOVE_CAST(Handler)(handler)));
+ }
+
+#define BOOST_ASIO_PRIVATE_INIT_WRAPPER_DEF(n) \
+ template \
+ void operator()( \
+ BOOST_ASIO_MOVE_ARG(Handler) handler, \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ { \
+ BOOST_ASIO_MOVE_CAST(Initiation)(initiation_)( \
+ allocator_binder< \
+ typename decay::type, Allocator>( \
+ allocator_, BOOST_ASIO_MOVE_CAST(Handler)(handler)), \
+ BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ \
+ template \
+ void operator()( \
+ BOOST_ASIO_MOVE_ARG(Handler) handler, \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) const \
+ { \
+ initiation_( \
+ allocator_binder< \
+ typename decay::type, Allocator>( \
+ allocator_, BOOST_ASIO_MOVE_CAST(Handler)(handler)), \
+ BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_INIT_WRAPPER_DEF)
+#undef BOOST_ASIO_PRIVATE_INIT_WRAPPER_DEF
+
+#endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ Allocator allocator_;
+ Initiation initiation_;
+ };
+
+#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template
+ static BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(T, Signature,
+ (async_initiate(
+ declval::type> >(),
+ declval().get(),
+ declval()...)))
+ initiate(
+ BOOST_ASIO_MOVE_ARG(Initiation) initiation,
+ BOOST_ASIO_MOVE_ARG(RawCompletionToken) token,
+ BOOST_ASIO_MOVE_ARG(Args)... args)
+ {
+ return async_initiate(
+ init_wrapper::type>(
+ token.get_allocator(),
+ BOOST_ASIO_MOVE_CAST(Initiation)(initiation)),
+ token.get(), BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+#else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template
+ static BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(T, Signature,
+ (async_initiate(
+ declval::type> >(),
+ declval().get())))
+ initiate(
+ BOOST_ASIO_MOVE_ARG(Initiation) initiation,
+ BOOST_ASIO_MOVE_ARG(RawCompletionToken) token)
+ {
+ return async_initiate(
+ init_wrapper::type>(
+ token.get_allocator(),
+ BOOST_ASIO_MOVE_CAST(Initiation)(initiation)),
+ token.get());
+ }
+
+#define BOOST_ASIO_PRIVATE_INITIATE_DEF(n) \
+ template \
+ static BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(T, Signature, \
+ (async_initiate( \
+ declval::type> >(), \
+ declval().get(), \
+ BOOST_ASIO_VARIADIC_MOVE_DECLVAL(n)))) \
+ initiate( \
+ BOOST_ASIO_MOVE_ARG(Initiation) initiation, \
+ BOOST_ASIO_MOVE_ARG(RawCompletionToken) token, \
+ BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ { \
+ return async_initiate( \
+ init_wrapper::type>( \
+ token.get_allocator(), \
+ BOOST_ASIO_MOVE_CAST(Initiation)(initiation)), \
+ token.get(), BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_INITIATE_DEF)
+#undef BOOST_ASIO_PRIVATE_INITIATE_DEF
+
+#endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+private:
+ async_result(const async_result&) BOOST_ASIO_DELETED;
+ async_result& operator=(const async_result&) BOOST_ASIO_DELETED;
+
+ async_result target_;
+};
+
+template class Associator,
+ typename T, typename Allocator, typename DefaultCandidate>
+struct associator,
+ DefaultCandidate>
+{
+ typedef typename Associator::type type;
+
+ static type get(const allocator_binder& b,
+ const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
+ {
+ return Associator::get(b.get(), c);
+ }
+};
+
+template
+struct associated_allocator<
+ allocator_binder,
+ Allocator1>
+{
+ typedef Allocator type;
+
+ static type get(const allocator_binder& b,
+ const Allocator1& = Allocator1()) BOOST_ASIO_NOEXCEPT
+ {
+ return b.get_allocator();
+ }
+};
+
+#endif // !defined(GENERATING_DOCUMENTATION)
+
+} // namespace asio
+} // namespace boost
+
+#include
+
+#endif // BOOST_ASIO_BIND_ALLOCATOR_HPP
diff --git a/test/Jamfile.v2 b/test/Jamfile.v2
index bb3f8f08..39eab6c9 100644
--- a/test/Jamfile.v2
+++ b/test/Jamfile.v2
@@ -86,8 +86,12 @@ test-suite "asio" :
[ link basic_waitable_timer.cpp : $(USE_SELECT) : basic_waitable_timer_select ]
[ link basic_writable_pipe.cpp ]
[ link basic_writable_pipe.cpp : $(USE_SELECT) : basic_writable_pipe_select ]
+ [ run bind_allocator.cpp ]
+ [ run bind_allocator.cpp : : : $(USE_SELECT) : bind_allocator_select ]
[ run bind_cancellation_slot.cpp ]
[ run bind_cancellation_slot.cpp : : : $(USE_SELECT) : bind_cancellation_slot_select ]
+ [ run bind_executor.cpp ]
+ [ run bind_executor.cpp : : : $(USE_SELECT) : bind_executor_select ]
[ run buffer.cpp ]
[ run buffer.cpp : : : $(USE_SELECT) : buffer_select ]
[ link buffer_registration.cpp ]
diff --git a/test/bind_allocator.cpp b/test/bind_allocator.cpp
new file mode 100644
index 00000000..6e27a2d0
--- /dev/null
+++ b/test/bind_allocator.cpp
@@ -0,0 +1,257 @@
+//
+// bind_allocator.cpp
+// ~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+// Disable autolinking for unit tests.
+#if !defined(BOOST_ALL_NO_LIB)
+#define BOOST_ALL_NO_LIB 1
+#endif // !defined(BOOST_ALL_NO_LIB)
+
+// Test that header file is self-contained.
+#include
+
+#include
+#include
+#include "unit_test.hpp"
+
+#if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
+# include
+#else // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
+# include
+#endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
+
+#if defined(BOOST_ASIO_HAS_BOOST_BIND)
+# include
+#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
+# include
+#endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
+
+using namespace boost::asio;
+
+#if defined(BOOST_ASIO_HAS_BOOST_BIND)
+namespace bindns = boost;
+#else // defined(BOOST_ASIO_HAS_BOOST_BIND)
+namespace bindns = std;
+#endif
+
+#if defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
+typedef deadline_timer timer;
+namespace chronons = boost::posix_time;
+#elif defined(BOOST_ASIO_HAS_CHRONO)
+typedef steady_timer timer;
+namespace chronons = boost::asio::chrono;
+#endif // defined(BOOST_ASIO_HAS_BOOST_DATE_TIME)
+
+template
+class test_allocator
+{
+public:
+ typedef T value_type;
+
+ explicit test_allocator(int* allocations)
+ : allocations_(allocations)
+ {
+ }
+
+ template
+ test_allocator(const test_allocator& other)
+ : allocations_(other.allocations_)
+ {
+ }
+
+ template
+ struct rebind
+ {
+ typedef test_allocator other;
+ };
+
+ bool operator==(const test_allocator&) const
+ {
+ return true;
+ }
+
+ bool operator!=(const test_allocator&) const
+ {
+ return false;
+ }
+
+ T* allocate(std::size_t n) const
+ {
+ ++(*allocations_);
+ return static_cast(::operator new(sizeof(T) * n));
+ }
+
+ void deallocate(T* p, std::size_t /*n*/) const
+ {
+ --(*allocations_);
+ ::operator delete(p);
+ }
+
+//private:
+ int* allocations_;
+};
+
+void increment(int* count)
+{
+ ++(*count);
+}
+
+void bind_allocator_to_function_object_test()
+{
+ io_context ioc;
+
+ int count = 0;
+ int allocations = 0;
+
+ timer t(ioc, chronons::seconds(1));
+ t.async_wait(
+ bind_allocator(
+ test_allocator(&allocations),
+ bindns::bind(&increment, &count)));
+
+ BOOST_ASIO_CHECK(count == 0);
+ BOOST_ASIO_CHECK(allocations == 1);
+
+ ioc.run();
+
+ BOOST_ASIO_CHECK(count == 1);
+ BOOST_ASIO_CHECK(allocations == 0);
+}
+
+struct incrementer_token_v1
+{
+ explicit incrementer_token_v1(int* c) : count(c) {}
+ int* count;
+};
+
+struct incrementer_handler_v1
+{
+ explicit incrementer_handler_v1(incrementer_token_v1 t) : count(t.count) {}
+ void operator()(boost::system::error_code){ increment(count); }
+ int* count;
+};
+
+namespace boost {
+namespace asio {
+
+template <>
+class async_result
+{
+public:
+ typedef incrementer_handler_v1 completion_handler_type;
+ typedef void return_type;
+ explicit async_result(completion_handler_type&) {}
+ return_type get() {}
+};
+
+} // namespace asio
+} // namespace boost
+
+void bind_allocator_to_completion_token_v1_test()
+{
+ io_context ioc;
+
+ int count = 0;
+ int allocations = 0;
+
+ timer t(ioc, chronons::seconds(1));
+ t.async_wait(
+ bind_allocator(
+ test_allocator(&allocations),
+ incrementer_token_v1(&count)));
+
+ BOOST_ASIO_CHECK(count == 0);
+ BOOST_ASIO_CHECK(allocations == 1);
+
+ ioc.run();
+
+ BOOST_ASIO_CHECK(count == 1);
+ BOOST_ASIO_CHECK(allocations == 0);
+}
+
+struct incrementer_token_v2
+{
+ explicit incrementer_token_v2(int* c) : count(c) {}
+ int* count;
+};
+
+namespace boost {
+namespace asio {
+
+template <>
+class async_result
+{
+public:
+ typedef void return_type;
+
+#if defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template
+ static void initiate(Initiation initiation,
+ incrementer_token_v2 token, BOOST_ASIO_MOVE_ARG(Args)... args)
+ {
+ initiation(bindns::bind(&increment, token.count),
+ BOOST_ASIO_MOVE_CAST(Args)(args)...);
+ }
+
+#else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+
+ template
+ static void initiate(Initiation initiation, incrementer_token_v2 token)
+ {
+ initiation(bindns::bind(&increment, token.count));
+ }
+
+#define BOOST_ASIO_PRIVATE_INITIATE_DEF(n) \
+ template \
+ static return_type initiate(Initiation initiation, \
+ incrementer_token_v2 token, BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ { \
+ initiation(bindns::bind(&increment, token.count), \
+ BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
+ } \
+ /**/
+ BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_INITIATE_DEF)
+#undef BOOST_ASIO_PRIVATE_INITIATE_DEF
+
+#endif // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
+};
+
+} // namespace asio
+} // namespace boost
+
+void bind_allocator_to_completion_token_v2_test()
+{
+ io_context ioc;
+
+ int count = 0;
+ int allocations = 0;
+
+ timer t(ioc, chronons::seconds(1));
+ t.async_wait(
+ bind_allocator(
+ test_allocator(&allocations),
+ incrementer_token_v2(&count)));
+
+ BOOST_ASIO_CHECK(count == 0);
+ BOOST_ASIO_CHECK(allocations == 1);
+
+ ioc.run();
+
+ BOOST_ASIO_CHECK(count == 1);
+ BOOST_ASIO_CHECK(allocations == 0);
+}
+
+BOOST_ASIO_TEST_SUITE
+(
+ "bind_allocator",
+ BOOST_ASIO_TEST_CASE(bind_allocator_to_function_object_test)
+ BOOST_ASIO_TEST_CASE(bind_allocator_to_completion_token_v1_test)
+ BOOST_ASIO_TEST_CASE(bind_allocator_to_completion_token_v2_test)
+)
diff --git a/test/bind_executor.cpp b/test/bind_executor.cpp
index 12574238..5d9dc373 100644
--- a/test/bind_executor.cpp
+++ b/test/bind_executor.cpp
@@ -103,7 +103,7 @@ public:
#else // defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
template
- static void initiate(Initiation initiation, incrementer_token token)
+ static void initiate(Initiation initiation, incrementer_token_v2 token)
{
initiation(bindns::bind(&increment, token.count));
}
@@ -111,7 +111,7 @@ public:
#define BOOST_ASIO_PRIVATE_INITIATE_DEF(n) \
template \
static return_type initiate(Initiation initiation, \
- incrementer_token token, BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
+ incrementer_token_v2 token, BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
{ \
initiation(bindns::bind(&increment, token.count), \
BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \