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