Add bind_front_handler:

This works similarly to bind_handler, and can help with reducing
template instantiations and compiler memory usage (since it does
not need to support placeholders).

A specialization for the most common case `void(error_code, size_t)`
is provided to minimize instantiation cost.
This commit is contained in:
Vinnie Falco committed 2018-11-21 12:47:38 -08:00
1 parent 868ca0f470
commit d581bcc348
6 files changed
+575 -80

No files matched your search

+48 -2
View File
@@ -56,7 +56,7 @@ template<class Handler, class... Args>
#if BOOST_BEAST_DOXYGEN
__implementation_defined__
#else
detail::bound_handler<
detail::bind_wrapper<
typename std::decay<Handler>::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<typename std::decay<
return detail::bind_wrapper<typename std::decay<
Handler>::type, Args...>(std::forward<
Handler>(handler), std::forward<Args>(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<class AsyncReadStream, class ReadHandler>
void
signal_aborted(AsyncReadStream& stream, ReadHandler&& handler)
{
boost::asio::post(
stream.get_executor(),
bind_front_handler(std::forward<ReadHandler>(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<class Handler, class... Args>
#if BOOST_BEAST_DOXYGEN
__implementation_defined__
#else
auto
#endif
bind_front_handler(Handler&& handler, Args&&... args) ->
detail::bind_front_wrapper<typename
std::decay<Handler>::type, Args...>
{
return detail::bind_front_wrapper<typename
std::decay<Handler>::type, Args...>(
std::forward<Handler>(handler),
std::forward<Args>(args)...);
}
} // beast
} // boost
+374 -26
View File
@@ -10,12 +10,14 @@
#ifndef BOOST_BEAST_DETAIL_BIND_HANDLER_HPP
#define BOOST_BEAST_DETAIL_BIND_HANDLER_HPP
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/detail/integer_sequence.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <boost/is_placeholder.hpp>
#include <functional>
#include <utility>
@@ -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 Handler, class... Args>
class bound_handler
class bind_wrapper
{
// Can't friend partial specializations,
// so we just friend the whole thing.
template<class T, class Executor>
friend struct boost::asio::associated_executor;
using args_type = std::tuple<
typename std::decay<Args>::type...>;
Handler h_;
args_type args_;
// Can't friend partial specializations,
// so we just friend the whole thing.
template<class T, class Executor>
friend struct boost::asio::associated_executor;
template<class Arg, class Vals>
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<S...>)
{
boost::ignore_unused(args);
h(std::get<S>(std::move(args))...);
w(std::get<S>(std::move(args))...);
}
template<
@@ -111,14 +108,14 @@ class bound_handler
static
void
invoke(
Handler& h,
Handler& w,
ArgsTuple& args,
ValsTuple&& vals,
index_sequence<S...>)
{
boost::ignore_unused(args);
boost::ignore_unused(vals);
h(extract(std::get<S>(std::move(args)),
w(extract(std::get<S>(std::move(args)),
std::forward<ValsTuple>(vals))...);
}
@@ -128,12 +125,12 @@ public:
using allocator_type =
boost::asio::associated_allocator_t<Handler>;
bound_handler(bound_handler&&) = default;
bound_handler(bound_handler const&) = delete;
bind_wrapper(bind_wrapper&&) = default;
bind_wrapper(bind_wrapper const&) = default;
template<class DeducedHandler>
explicit
bound_handler(
bind_wrapper(
DeducedHandler&& handler, Args&&... args)
: h_(std::forward<DeducedHandler>(handler))
, args_(std::forward<Args>(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<class Function>
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<class... Values>
@@ -183,35 +180,386 @@ public:
}
};
//------------------------------------------------------------------------------
template<class Handler, class... Args>
class bind_front_wrapper;
// 0-arg specialization
template<class Handler>
class bind_front_wrapper<Handler>
{
Handler h_;
// Can't friend partial specializations,
// so we just friend the whole thing.
template<class T, class Executor>
friend struct boost::asio::associated_executor;
public:
using result_type = void;
using allocator_type =
boost::asio::associated_allocator_t<Handler>;
bind_front_wrapper(bind_front_wrapper&&) = default;
bind_front_wrapper(bind_front_wrapper const&) = default;
template<class DeducedHandler>
explicit
bind_front_wrapper(DeducedHandler&& handler)
: h_(std::forward<DeducedHandler>(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<class Function>
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<class... Ts>
void operator()(Ts&&... ts)
{
h_(std::forward<Ts>(ts)...);
}
};
// 1-arg specialization
template<class Handler, class Arg>
class bind_front_wrapper<Handler, Arg>
{
Handler h_;
typename std::decay<Arg>::type arg_;
// Can't friend partial specializations,
// so we just friend the whole thing.
template<class T, class Executor>
friend struct boost::asio::associated_executor;
public:
using result_type = void;
using allocator_type =
boost::asio::associated_allocator_t<Handler>;
bind_front_wrapper(bind_front_wrapper&&) = default;
bind_front_wrapper(bind_front_wrapper const&) = default;
template<class DeducedHandler>
bind_front_wrapper(
DeducedHandler&& handler, Arg&& arg)
: h_(std::forward<DeducedHandler>(handler))
, arg_(std::forward<Arg>(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<class Function>
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<class... Ts>
void operator()(Ts&&... ts)
{
h_( std::forward<Arg>(arg_),
std::forward<Ts>(ts)...);
}
};
// 2-arg specialization
template<class Handler, class Arg1, class Arg2>
class bind_front_wrapper<Handler, Arg1, Arg2>
{
Handler h_;
typename std::decay<Arg1>::type arg1_;
typename std::decay<Arg2>::type arg2_;
// Can't friend partial specializations,
// so we just friend the whole thing.
template<class T, class Executor>
friend struct boost::asio::associated_executor;
public:
using result_type = void;
using allocator_type =
boost::asio::associated_allocator_t<Handler>;
bind_front_wrapper(bind_front_wrapper&&) = default;
bind_front_wrapper(bind_front_wrapper const&) = default;
template<class DeducedHandler>
bind_front_wrapper(DeducedHandler&& handler,
Arg1&& arg1, Arg2&& arg2)
: h_(std::forward<DeducedHandler>(handler))
, arg1_(std::forward<Arg1>(arg1))
, arg2_(std::forward<Arg2>(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<class Function>
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<class... Ts>
void operator()(Ts&&... ts)
{
h_( std::forward<Arg1>(arg1_),
std::forward<Arg2>(arg2_),
std::forward<Ts>(ts)...);
}
};
// 3+ arg specialization
template<class Handler,
class Arg1, class Arg2, class Arg3, class... Args>
class bind_front_wrapper<Handler, Arg1, Arg2, Arg3, Args...>
{
using args_type = std::tuple<
typename std::decay<Arg1>::type,
typename std::decay<Arg2>::type,
typename std::decay<Arg3>::type,
typename std::decay<Args>::type...>;
Handler h_;
args_type args_;
// Can't friend partial specializations,
// so we just friend the whole thing.
template<class T, class Executor>
friend struct boost::asio::associated_executor;
template<std::size_t... I, class... Ts>
void
invoke(
boost::mp11::index_sequence<I...>,
Ts&&... ts)
{
h_( std::get<I>(std::move(args_))...,
std::forward<Ts>(ts)...);
}
public:
using result_type = void;
using allocator_type =
boost::asio::associated_allocator_t<Handler>;
bind_front_wrapper(bind_front_wrapper&&) = default;
bind_front_wrapper(bind_front_wrapper const&) = default;
template<class DeducedHandler>
bind_front_wrapper(DeducedHandler&& handler,
Arg1&& arg1, Arg2&& arg2, Arg3&& arg3,
Args&&... args)
: h_(std::forward<DeducedHandler>(handler))
, args_(
std::forward<Arg1>(arg1),
std::forward<Arg2>(arg2),
std::forward<Arg3>(arg3),
std::forward<Args>(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<class Function>
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<class... Ts>
void operator()(Ts&&... ts)
{
invoke(
boost::mp11::index_sequence_for<
Arg1, Arg2, Arg3, Args...>{},
std::forward<Ts>(ts)...);
}
};
//------------------------------------------------------------------------------
// specialization for the most common case,
// to reduce instantiation time and memory.
template<class Handler>
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<Handler>;
bind_front_wrapper(bind_front_wrapper&&) = default;
bind_front_wrapper(bind_front_wrapper const&) = default;
template<class DeducedHandler>
bind_front_wrapper(DeducedHandler&& handler,
error_code ec, std::size_t n)
: h_(std::forward<DeducedHandler>(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<class Function>
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<class Handler, class... Args, class Executor>
struct associated_executor<
beast::detail::bound_handler<Handler, Args...>, Executor>
beast::detail::bind_wrapper<Handler, Args...>, Executor>
{
using type = typename
associated_executor<Handler, Executor>::type;
static
type
get(beast::detail::bound_handler<Handler, Args...> const& h,
get(beast::detail::bind_wrapper<Handler, Args...> const& w,
Executor const& ex = Executor()) noexcept
{
return associated_executor<
Handler, Executor>::get(h.h_, ex);
Handler, Executor>::get(w.h_, ex);
}
};
template<class Handler, class... Args, class Executor>
struct associated_executor<
beast::detail::bind_front_wrapper<Handler, Args...>, Executor>
{
using type = typename
associated_executor<Handler, Executor>::type;
static
type
get(beast::detail::bind_front_wrapper<Handler, Args...> const& w,
Executor const& ex = Executor()) noexcept
{
return associated_executor<
Handler, Executor>::get(w.h_, ex);
}
};
} // asio
} // boost
namespace std {
template<class Handler, class... Args>
void
bind(boost::beast::detail::bound_handler<
bind(boost::beast::detail::bind_wrapper<
Handler, Args...>, ...) = delete;
template<class Handler, class... Args>
void
bind(boost::beast::detail::bind_front_wrapper<
Handler, Args...>, ...) = delete;
} // std
#endif