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();
}
};