diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9e8cda..20299dbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Version 191: + +* Add bind_front_handler + +-------------------------------------------------------------------------------- + Version 190: * Add missing includes to convenience headers diff --git a/doc/qbk/03_core/5_composed.qbk b/doc/qbk/03_core/5_composed.qbk index 9881db7e..a5451b13 100644 --- a/doc/qbk/03_core/5_composed.qbk +++ b/doc/qbk/03_core/5_composed.qbk @@ -51,6 +51,17 @@ composed operations: handler, whose associated allocator and associated executor will will be the same as those of the original handler. ]] +[[ + [link beast.ref.boost__beast__bind_front_handler `bind_front_handler`] +][ + This function creates a new handler which, when invoked, calls + the original handler with the list of bound arguments, followed + by the list of invoked arguments. Placeholders are not supported. + + The passed handler and arguments are forwarded into the returned + handler, whose associated allocator and associated executor will + will be the same as those of the original handler. +]] [[ [link beast.ref.boost__beast__handler_ptr `handler_ptr`] ][ diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index e051551e..1459744f 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -213,6 +213,7 @@ Functions bind_handler + bind_front_handler buffers buffers_cat buffers_front diff --git a/include/boost/beast/core/bind_handler.hpp b/include/boost/beast/core/bind_handler.hpp index 1a1b3e40..89fb8437 100644 --- a/include/boost/beast/core/bind_handler.hpp +++ b/include/boost/beast/core/bind_handler.hpp @@ -56,7 +56,7 @@ template #if BOOST_BEAST_DOXYGEN __implementation_defined__ #else -detail::bound_handler< +detail::bind_wrapper< typename std::decay::type, Args...> #endif bind_handler(Handler&& handler, Args&&... args) @@ -67,11 +67,57 @@ bind_handler(Handler&& handler, Args&&... args) Handler, void(Args...)>::value, "Handler requirements not met"); #endif - return detail::bound_handler::type, Args...>(std::forward< Handler>(handler), std::forward(args)...); } +/** Bind parameters to a completion handler, creating a new handler. + + This function creates a new handler which, when invoked, calls + the original handler with the list of bound arguments. Any + parameters passed in the invocation will be forwarded in + the parameter list after the bound arguments. + + The passed handler and arguments are forwarded into the returned + handler, whose associated allocator and associated executor will + will be the same as those of the original handler. + + Example: + + @code + template + void + signal_aborted(AsyncReadStream& stream, ReadHandler&& handler) + { + boost::asio::post( + stream.get_executor(), + bind_front_handler(std::forward(handler), + boost::asio::error::operation_aborted, 0)); + } + @endcode + + @param handler The handler to wrap. + + @param args A list of arguments to bind to the handler. + The arguments are forwarded into the returned object. +*/ +template +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +auto +#endif +bind_front_handler(Handler&& handler, Args&&... args) -> + detail::bind_front_wrapper::type, Args...> +{ + return detail::bind_front_wrapper::type, Args...>( + std::forward(handler), + std::forward(args)...); +} + } // beast } // boost diff --git a/include/boost/beast/core/detail/bind_handler.hpp b/include/boost/beast/core/detail/bind_handler.hpp index 8bacc921..ad39de13 100644 --- a/include/boost/beast/core/detail/bind_handler.hpp +++ b/include/boost/beast/core/detail/bind_handler.hpp @@ -10,12 +10,14 @@ #ifndef BOOST_BEAST_DETAIL_BIND_HANDLER_HPP #define BOOST_BEAST_DETAIL_BIND_HANDLER_HPP +#include #include #include #include #include #include #include +#include #include #include #include @@ -24,25 +26,20 @@ namespace boost { namespace beast { namespace detail { -/* Nullary handler that calls Handler with bound arguments. - - The bound handler provides the same io_context execution - guarantees as the original handler. -*/ template -class bound_handler +class bind_wrapper { - // Can't friend partial specializations, - // so we just friend the whole thing. - template - friend struct boost::asio::associated_executor; - using args_type = std::tuple< typename std::decay::type...>; Handler h_; args_type args_; + // Can't friend partial specializations, + // so we just friend the whole thing. + template + friend struct boost::asio::associated_executor; + template static typename std::enable_if< @@ -95,13 +92,13 @@ class bound_handler static void invoke( - Handler& h, + Handler& w, ArgsTuple& args, std::tuple<>&&, index_sequence) { boost::ignore_unused(args); - h(std::get(std::move(args))...); + w(std::get(std::move(args))...); } template< @@ -111,14 +108,14 @@ class bound_handler static void invoke( - Handler& h, + Handler& w, ArgsTuple& args, ValsTuple&& vals, index_sequence) { boost::ignore_unused(args); boost::ignore_unused(vals); - h(extract(std::get(std::move(args)), + w(extract(std::get(std::move(args)), std::forward(vals))...); } @@ -128,12 +125,12 @@ public: using allocator_type = boost::asio::associated_allocator_t; - bound_handler(bound_handler&&) = default; - bound_handler(bound_handler const&) = delete; + bind_wrapper(bind_wrapper&&) = default; + bind_wrapper(bind_wrapper const&) = default; template explicit - bound_handler( + bind_wrapper( DeducedHandler&& handler, Args&&... args) : h_(std::forward(handler)) , args_(std::forward(args)...) @@ -148,18 +145,18 @@ public: friend bool - asio_handler_is_continuation(bound_handler* h) + asio_handler_is_continuation(bind_wrapper* w) { using boost::asio::asio_handler_is_continuation; - return asio_handler_is_continuation(std::addressof(h->h_)); + return asio_handler_is_continuation(std::addressof(w->h_)); } template friend - void asio_handler_invoke(Function&& f, bound_handler* h) + void asio_handler_invoke(Function&& f, bind_wrapper* w) { using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(h->h_)); + asio_handler_invoke(f, std::addressof(w->h_)); } template @@ -183,35 +180,386 @@ public: } }; +//------------------------------------------------------------------------------ + +template +class bind_front_wrapper; + +// 0-arg specialization +template +class bind_front_wrapper +{ + Handler h_; + + // Can't friend partial specializations, + // so we just friend the whole thing. + template + friend struct boost::asio::associated_executor; + +public: + using result_type = void; + + using allocator_type = + boost::asio::associated_allocator_t; + + bind_front_wrapper(bind_front_wrapper&&) = default; + bind_front_wrapper(bind_front_wrapper const&) = default; + + template + explicit + bind_front_wrapper(DeducedHandler&& handler) + : h_(std::forward(handler)) + { + } + + allocator_type + get_allocator() const noexcept + { + return boost::asio::get_associated_allocator(h_); + } + + friend + bool + asio_handler_is_continuation(bind_front_wrapper* w) + { + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation(std::addressof(w->h_)); + } + + template + friend + void asio_handler_invoke(Function&& f, bind_front_wrapper* w) + { + using boost::asio::asio_handler_invoke; + asio_handler_invoke(f, std::addressof(w->h_)); + } + + template + void operator()(Ts&&... ts) + { + h_(std::forward(ts)...); + } +}; + +// 1-arg specialization +template +class bind_front_wrapper +{ + Handler h_; + typename std::decay::type arg_; + + // Can't friend partial specializations, + // so we just friend the whole thing. + template + friend struct boost::asio::associated_executor; + +public: + using result_type = void; + + using allocator_type = + boost::asio::associated_allocator_t; + + bind_front_wrapper(bind_front_wrapper&&) = default; + bind_front_wrapper(bind_front_wrapper const&) = default; + + template + bind_front_wrapper( + DeducedHandler&& handler, Arg&& arg) + : h_(std::forward(handler)) + , arg_(std::forward(arg)) + { + } + + allocator_type + get_allocator() const noexcept + { + return boost::asio::get_associated_allocator(h_); + } + + friend + bool + asio_handler_is_continuation(bind_front_wrapper* w) + { + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation(std::addressof(w->h_)); + } + + template + friend + void asio_handler_invoke(Function&& f, bind_front_wrapper* w) + { + using boost::asio::asio_handler_invoke; + asio_handler_invoke(f, std::addressof(w->h_)); + } + + template + void operator()(Ts&&... ts) + { + h_( std::forward(arg_), + std::forward(ts)...); + } +}; + +// 2-arg specialization +template +class bind_front_wrapper +{ + Handler h_; + typename std::decay::type arg1_; + typename std::decay::type arg2_; + + // Can't friend partial specializations, + // so we just friend the whole thing. + template + friend struct boost::asio::associated_executor; + +public: + using result_type = void; + + using allocator_type = + boost::asio::associated_allocator_t; + + bind_front_wrapper(bind_front_wrapper&&) = default; + bind_front_wrapper(bind_front_wrapper const&) = default; + + template + bind_front_wrapper(DeducedHandler&& handler, + Arg1&& arg1, Arg2&& arg2) + : h_(std::forward(handler)) + , arg1_(std::forward(arg1)) + , arg2_(std::forward(arg2)) + { + } + + allocator_type + get_allocator() const noexcept + { + return boost::asio::get_associated_allocator(h_); + } + + friend + bool + asio_handler_is_continuation(bind_front_wrapper* w) + { + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation(std::addressof(w->h_)); + } + + template + friend + void asio_handler_invoke(Function&& f, bind_front_wrapper* w) + { + using boost::asio::asio_handler_invoke; + asio_handler_invoke(f, std::addressof(w->h_)); + } + + template + void operator()(Ts&&... ts) + { + h_( std::forward(arg1_), + std::forward(arg2_), + std::forward(ts)...); + } +}; + +// 3+ arg specialization +template +class bind_front_wrapper +{ + using args_type = std::tuple< + typename std::decay::type, + typename std::decay::type, + typename std::decay::type, + typename std::decay::type...>; + + Handler h_; + args_type args_; + + // Can't friend partial specializations, + // so we just friend the whole thing. + template + friend struct boost::asio::associated_executor; + + template + void + invoke( + boost::mp11::index_sequence, + Ts&&... ts) + { + h_( std::get(std::move(args_))..., + std::forward(ts)...); + } + +public: + using result_type = void; + + using allocator_type = + boost::asio::associated_allocator_t; + + bind_front_wrapper(bind_front_wrapper&&) = default; + bind_front_wrapper(bind_front_wrapper const&) = default; + + template + bind_front_wrapper(DeducedHandler&& handler, + Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, + Args&&... args) + : h_(std::forward(handler)) + , args_( + std::forward(arg1), + std::forward(arg2), + std::forward(arg3), + std::forward(args)...) + { + } + + allocator_type + get_allocator() const noexcept + { + return boost::asio::get_associated_allocator(h_); + } + + friend + bool + asio_handler_is_continuation(bind_front_wrapper* w) + { + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation(std::addressof(w->h_)); + } + + template + friend + void asio_handler_invoke(Function&& f, bind_front_wrapper* w) + { + using boost::asio::asio_handler_invoke; + asio_handler_invoke(f, std::addressof(w->h_)); + } + + template + void operator()(Ts&&... ts) + { + invoke( + boost::mp11::index_sequence_for< + Arg1, Arg2, Arg3, Args...>{}, + std::forward(ts)...); + } +}; + +//------------------------------------------------------------------------------ + +// specialization for the most common case, +// to reduce instantiation time and memory. +template +class bind_front_wrapper< + Handler, error_code, std::size_t> +{ + Handler h_; + error_code ec_; + std::size_t n_; + +public: + using result_type = void; + + using allocator_type = + boost::asio::associated_allocator_t; + + bind_front_wrapper(bind_front_wrapper&&) = default; + bind_front_wrapper(bind_front_wrapper const&) = default; + + template + bind_front_wrapper(DeducedHandler&& handler, + error_code ec, std::size_t n) + : h_(std::forward(handler)) + , ec_(ec) + , n_(n) + { + } + + allocator_type + get_allocator() const noexcept + { + return boost::asio::get_associated_allocator(h_); + } + + friend + bool + asio_handler_is_continuation(bind_front_wrapper* w) + { + using boost::asio::asio_handler_is_continuation; + return asio_handler_is_continuation(std::addressof(w->h_)); + } + + template + friend + void asio_handler_invoke(Function&& f, bind_front_wrapper* w) + { + using boost::asio::asio_handler_invoke; + asio_handler_invoke(f, std::addressof(w->h_)); + } + + void operator()() + { + h_(ec_, n_); + } +}; + } // detail } // beast +//------------------------------------------------------------------------------ + namespace asio { + template struct associated_executor< - beast::detail::bound_handler, Executor> + beast::detail::bind_wrapper, Executor> { using type = typename associated_executor::type; static type - get(beast::detail::bound_handler const& h, + get(beast::detail::bind_wrapper const& w, Executor const& ex = Executor()) noexcept { return associated_executor< - Handler, Executor>::get(h.h_, ex); + Handler, Executor>::get(w.h_, ex); } }; + +template +struct associated_executor< + beast::detail::bind_front_wrapper, Executor> +{ + using type = typename + associated_executor::type; + + static + type + get(beast::detail::bind_front_wrapper const& w, + Executor const& ex = Executor()) noexcept + { + return associated_executor< + Handler, Executor>::get(w.h_, ex); + } +}; + } // asio } // boost namespace std { + template void -bind(boost::beast::detail::bound_handler< +bind(boost::beast::detail::bind_wrapper< Handler, Args...>, ...) = delete; + +template +void +bind(boost::beast::detail::bind_front_wrapper< + Handler, Args...>, ...) = delete; + } // std #endif diff --git a/test/beast/core/bind_handler.cpp b/test/beast/core/bind_handler.cpp index 685665c5..bd8c4e2a 100644 --- a/test/beast/core/bind_handler.cpp +++ b/test/beast/core/bind_handler.cpp @@ -32,34 +32,7 @@ public: } }; -#if 0 - // This function should fail to compile - void - failStdBind() - { - std::bind(bind_handler(handler<>{})); - } -#endif - - void - callback(int v) - { - BEAST_EXPECT(v == 42); - } - - void - testPlaceholders() - { - namespace ph = std::placeholders; - - bind_handler(handler<>{})(); - bind_handler(handler{}, 1)(); - bind_handler(handler{}, 1, "hello")(); - bind_handler(handler{}, ph::_1)(1); - bind_handler(handler{}, ph::_1, ph::_2)(1, "hello"); - } - - struct copyable_handler + struct copyable { template void @@ -68,44 +41,154 @@ public: } }; - void - testAsioHandlerInvoke() - { - // make sure things compile, also can set a - // breakpoint in asio_handler_invoke to make sure - // it is instantiated. - boost::asio::io_context ioc; - boost::asio::io_service::strand s{ioc}; - test::stream ts{ioc}; - boost::asio::post(ioc.get_executor(), - s.wrap(copyable_handler{})); - } - struct move_only { move_only() = default; move_only(move_only&&) = default; move_only(move_only const&) = delete; + void operator()() const{}; }; void - testMoveOnly() + callback(int v) { - bind_handler([](move_only){}, move_only{})(); - bind_handler([](move_only){}, std::placeholders::_1)(move_only{}); - bind_handler([](move_only, move_only){}, move_only{}, std::placeholders::_1)(move_only{}); + BEAST_EXPECT(v == 42); + } + +#if 0 + // This function should fail to compile + void + failStdBind() + { + std::bind(bind_handler(handler<>{})); + } + void + failStdBindFront() + { + std::bind(bind_front_handler(handler<>{})); + } +#endif + + void + testBindHandler() + { + // invocation + { + auto f = bind_handler(std::bind( + &bind_handler_test::callback, this, + std::placeholders::_1), 42); + f(); + + } + + // placeholders + { + namespace ph = std::placeholders; + bind_handler(handler<>{})(); + bind_handler(handler{}, 1)(); + bind_handler(handler{}, 1, "hello")(); + bind_handler(handler{}, ph::_1)(1); + bind_handler(handler{}, ph::_1, ph::_2)(1, "hello"); + } + + // move-only + { + bind_handler([](move_only){}, move_only{})(); + bind_handler([](move_only){}, std::placeholders::_1)(move_only{}); + bind_handler([](move_only, move_only){}, move_only{}, std::placeholders::_1)(move_only{}); + } + + // asio_handler_invoke + { + // make sure things compile, also can set a + // breakpoint in asio_handler_invoke to make sure + // it is instantiated. + boost::asio::io_context ioc; + boost::asio::strand< + boost::asio::io_context::executor_type> s{ + ioc.get_executor()}; + test::stream ts{ioc}; + boost::asio::post(s, + bind_handler(copyable{}, 42)); + } + } + + void + testBindFrontHandler() + { + // invocation + { + bind_front_handler( + std::bind( + &bind_handler_test::callback, + this, + 42)); + + bind_front_handler( + std::bind( + &bind_handler_test::callback, + this, + std::placeholders::_1), 42); + + bind_front_handler( + std::bind( + &bind_handler_test::callback, + this, + std::placeholders::_1))(42); + + bind_front_handler( + bind_front_handler( + std::bind( + &bind_handler_test::callback, + this, + std::placeholders::_1)), + 42); + + bind_front_handler( + bind_front_handler( + std::bind( + &bind_handler_test::callback, + this, + std::placeholders::_1)))(42); + } + + // move-only + { + bind_front_handler([]{}); + } + + // specializations + { + bind_front_handler(copyable{}); + bind_front_handler(copyable{}, 1); + bind_front_handler(copyable{}, 1, 2); + bind_front_handler(copyable{}, 1, 2, 3); + bind_front_handler(copyable{}, 1, 2, 3, 4); + + bind_front_handler(copyable{}, + error_code{}, std::size_t(4)); + } + + // asio_handler_invoke + { + // make sure things compile, also can set a + // breakpoint in asio_handler_invoke to make sure + // it is instantiated. + boost::asio::io_context ioc; + boost::asio::strand< + boost::asio::io_context::executor_type> s{ + ioc.get_executor()}; + test::stream ts{ioc}; + boost::asio::post(s, + bind_front_handler(copyable{}, 42)); + } } void run() override { - auto f = bind_handler(std::bind( - &bind_handler_test::callback, this, - std::placeholders::_1), 42); - f(); - testPlaceholders(); - testAsioHandlerInvoke(); - testMoveOnly(); + testBindHandler(); + testBindFrontHandler(); } };