diff --git a/CHANGELOG.md b/CHANGELOG.md index d00f0da2..813eee6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Version 202 * Refactor async_op_base * Use async_op_base * async_op_base is a public interface +* Add tests for bind_back_handler -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/detail/bind_handler.hpp b/include/boost/beast/core/detail/bind_handler.hpp index 40e20da9..e629faff 100644 --- a/include/boost/beast/core/detail/bind_handler.hpp +++ b/include/boost/beast/core/detail/bind_handler.hpp @@ -121,7 +121,7 @@ class bind_wrapper } public: - using result_type = void; + using result_type = void; // asio needs this bind_wrapper(bind_wrapper&&) = default; bind_wrapper(bind_wrapper const&) = default; @@ -222,268 +222,11 @@ class bind_front_wrapper; // //------------------------------------------------------------------------------ -// 0-arg specialization -template -class bind_front_wrapper +template +class bind_front_wrapper { Handler h_; - - template - friend struct net::associated_executor; - -public: - using result_type = void; - - bind_front_wrapper(bind_front_wrapper&&) = default; - bind_front_wrapper(bind_front_wrapper const&) = default; - - template - explicit - bind_front_wrapper(Handler_&& handler) - : h_(std::forward(handler)) - { - } - - template - void operator()(Ts&&... ts) - { - h_(std::forward(ts)...); - } - - // - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return net::get_associated_allocator(h_); - } - - template - friend - void asio_handler_invoke( - Function&& f, bind_front_wrapper* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } - - friend - bool asio_handler_is_continuation( - bind_front_wrapper* op) - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - friend - void* asio_handler_allocate( - std::size_t size, bind_front_wrapper* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, bind_front_wrapper* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } -}; - -//------------------------------------------------------------------------------ - -// 1-arg specialization -template -class bind_front_wrapper -{ - Handler h_; - Arg arg_; - - template - friend struct net::associated_executor; - -public: - using result_type = void; - - bind_front_wrapper(bind_front_wrapper&&) = default; - bind_front_wrapper(bind_front_wrapper const&) = default; - - template< - class Handler_, - class Arg_> - bind_front_wrapper( - Handler_&& handler, - Arg_&& arg) - : h_(std::forward(handler)) - , arg_(std::forward(arg)) - { - } - - template - void operator()(Ts&&... ts) - { - h_( std::move(arg_), - std::forward(ts)...); - } - - // - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return net::get_associated_allocator(h_); - } - - template - friend - void asio_handler_invoke( - Function&& f, bind_front_wrapper* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } - - friend - bool asio_handler_is_continuation( - bind_front_wrapper* op) - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - friend - void* asio_handler_allocate( - std::size_t size, bind_front_wrapper* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, bind_front_wrapper* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } -}; - -//------------------------------------------------------------------------------ - -// 2-arg specialization -template -class bind_front_wrapper -{ - Handler h_; - Arg1 arg1_; - Arg2 arg2_; - - template - friend struct net::associated_executor; - -public: - using result_type = void; - - bind_front_wrapper(bind_front_wrapper&&) = default; - bind_front_wrapper(bind_front_wrapper const&) = default; - - template< - class Handler_, - class Arg1_, - class Arg2_> - bind_front_wrapper( - Handler_&& handler, - Arg1_&& arg1, - Arg2_&& arg2) - : h_(std::forward(handler)) - , arg1_(std::forward(arg1)) - , arg2_(std::forward(arg2)) - { - } - - template - void operator()(Ts&&... ts) - { - h_( std::move(arg1_), - std::move(arg2_), - std::forward(ts)...); - } - - // - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return net::get_associated_allocator(h_); - } - - template - friend - void asio_handler_invoke( - Function&& f, bind_front_wrapper* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } - - friend - bool asio_handler_is_continuation( - bind_front_wrapper* op) - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - friend - void* asio_handler_allocate( - std::size_t size, bind_front_wrapper* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, bind_front_wrapper* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } -}; - -//------------------------------------------------------------------------------ - -// 3+ arg specialization -template< - class Handler, - class Arg1, class Arg2, class Arg3, - class... Args> -class bind_front_wrapper< - Handler, Arg1, Arg2, Arg3, Args...> -{ - Handler h_; - detail::tuple< - Arg1, Arg2, Arg3, Args...> args_; + detail::tuple args_; template friend struct net::associated_executor; @@ -499,27 +242,17 @@ class bind_front_wrapper< } public: - using result_type = void; + using result_type = void; // asio needs this bind_front_wrapper(bind_front_wrapper&&) = default; bind_front_wrapper(bind_front_wrapper const&) = default; - template< - class Handler_, - class Arg1_, class Arg2_, class Arg3_, - class... Args_> + template bind_front_wrapper( Handler_&& 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)...) + , args_(std::forward(args)...) { } @@ -527,8 +260,7 @@ public: void operator()(Ts&&... ts) { invoke( - mp11::index_sequence_for< - Arg1, Arg2, Arg3, Args...>{}, + mp11::index_sequence_for{}, std::forward(ts)...); } @@ -596,7 +328,7 @@ class bind_front_wrapper< std::size_t n_; public: - using result_type = void; + using result_type = void; // asio needs this bind_front_wrapper(bind_front_wrapper&&) = default; bind_front_wrapper(bind_front_wrapper const&) = default; @@ -670,268 +402,11 @@ public: // //------------------------------------------------------------------------------ -// 0-arg specialization -template -class bind_back_wrapper +template +class bind_back_wrapper { Handler h_; - - template - friend struct net::associated_executor; - -public: - using result_type = void; - - bind_back_wrapper(bind_back_wrapper&&) = default; - bind_back_wrapper(bind_back_wrapper const&) = default; - - template - explicit - bind_back_wrapper(Handler_&& handler) - : h_(std::forward(handler)) - { - } - - template - void operator()(Ts&&... ts) - { - h_(std::forward(ts)...); - } - - // - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return net::get_associated_allocator(h_); - } - - template - friend - void asio_handler_invoke( - Function&& f, bind_back_wrapper* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } - - friend - bool asio_handler_is_continuation( - bind_back_wrapper* op) - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - friend - void* asio_handler_allocate( - std::size_t size, bind_back_wrapper* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, bind_back_wrapper* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } -}; - -//------------------------------------------------------------------------------ - -// 1-arg specialization -template -class bind_back_wrapper -{ - Handler h_; - Arg arg_; - - template - friend struct net::associated_executor; - -public: - using result_type = void; - - bind_back_wrapper(bind_back_wrapper&&) = default; - bind_back_wrapper(bind_back_wrapper const&) = default; - - template< - class Handler_, - class Arg_> - bind_back_wrapper( - Handler_&& handler, - Arg_&& arg) - : h_(std::forward(handler)) - , arg_(std::forward(arg)) - { - } - - template - void operator()(Ts&&... ts) - { - h_( std::forward(ts)..., - std::move(arg_)); - } - - // - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return net::get_associated_allocator(h_); - } - - template - friend - void asio_handler_invoke( - Function&& f, bind_back_wrapper* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } - - friend - bool asio_handler_is_continuation( - bind_back_wrapper* op) - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - friend - void* asio_handler_allocate( - std::size_t size, bind_back_wrapper* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, bind_back_wrapper* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } -}; - -//------------------------------------------------------------------------------ - -// 2-arg specialization -template -class bind_back_wrapper -{ - Handler h_; - Arg1 arg1_; - Arg2 arg2_; - - template - friend struct net::associated_executor; - -public: - using result_type = void; - - bind_back_wrapper(bind_back_wrapper&&) = default; - bind_back_wrapper(bind_back_wrapper const&) = default; - - template< - class Handler_, - class Arg1_, - class Arg2_> - bind_back_wrapper( - Handler_&& handler, - Arg1_&& arg1, - Arg2_&& arg2) - : h_(std::forward(handler)) - , arg1_(std::forward(arg1)) - , arg2_(std::forward(arg2)) - { - } - - template - void operator()(Ts&&... ts) - { - h_( std::forward(ts)..., - std::move(arg1_), - std::move(arg2_)); - } - - // - - using allocator_type = - net::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return net::get_associated_allocator(h_); - } - - template - friend - void asio_handler_invoke( - Function&& f, bind_back_wrapper* op) - { - using net::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } - - friend - bool asio_handler_is_continuation( - bind_back_wrapper* op) - { - using net::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - friend - void* asio_handler_allocate( - std::size_t size, bind_back_wrapper* op) - { - using net::asio_handler_allocate; - return asio_handler_allocate( - size, std::addressof(op->h_)); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, bind_back_wrapper* op) - { - using net::asio_handler_deallocate; - asio_handler_deallocate( - p, size, std::addressof(op->h_)); - } -}; - -//------------------------------------------------------------------------------ - -// 3+ arg specialization -template< - class Handler, - class Arg1, class Arg2, class Arg3, - class... Args> -class bind_back_wrapper< - Handler, Arg1, Arg2, Arg3, Args...> -{ - Handler h_; - detail::tuple< - Arg1, Arg2, Arg3, Args...> args_; + detail::tuple args_; template friend struct net::associated_executor; @@ -947,27 +422,15 @@ class bind_back_wrapper< } public: - using result_type = void; + using result_type = void; // asio needs this bind_back_wrapper(bind_back_wrapper&&) = default; bind_back_wrapper(bind_back_wrapper const&) = default; - template< - class Handler_, - class Arg1_, class Arg2_, class Arg3_, - class... Args_> - bind_back_wrapper( - Handler_&& handler, - Arg1_&& arg1, - Arg2_&& arg2, - Arg3_&& arg3, - Args_&&... args) + template + bind_back_wrapper(Handler_&& handler, Args_&&... args) : h_(std::forward(handler)) - , args_( - std::forward(arg1), - std::forward(arg2), - std::forward(arg3), - std::forward(args)...) + , args_(std::forward(args)...) { } @@ -975,8 +438,7 @@ public: void operator()(Ts&&... ts) { invoke( - mp11::index_sequence_for< - Arg1, Arg2, Arg3, Args...>{}, + mp11::index_sequence_for{}, std::forward(ts)...); } @@ -1044,7 +506,7 @@ class bind_back_wrapper< std::size_t n_; public: - using result_type = void; + using result_type = void; // asio needs this bind_back_wrapper(bind_back_wrapper&&) = default; bind_back_wrapper(bind_back_wrapper const&) = default; diff --git a/test/beast/core/bind_handler.cpp b/test/beast/core/bind_handler.cpp index 8d5e65f8..f5f9ecfa 100644 --- a/test/beast/core/bind_handler.cpp +++ b/test/beast/core/bind_handler.cpp @@ -10,6 +10,8 @@ // Test that header file is self-contained. #include +#include "test_handler.hpp" + #include #include #include @@ -28,8 +30,6 @@ namespace boost { namespace beast { -//------------------------------------------------------------------------------ - class bind_handler_test : public unit_test::suite { public: @@ -42,54 +42,6 @@ public: } }; - template - void - signal_aborted (AsyncReadStream& stream, ReadHandler&& handler) - { - net::post( - stream.get_executor(), - bind_handler (std::forward (handler), - net::error::operation_aborted, 0)); - } - - template - void - signal_eof (AsyncReadStream& stream, ReadHandler&& handler) - { - net::post( - stream.get_executor(), - bind_front_handler (std::forward (handler), - net::error::eof, 0)); - } - - template - void - signal_unreachable (AsyncReadStream& stream, ReadHandler&& handler) - { - net::post( - stream.get_executor(), - bind_back_handler (std::forward (handler), - net::error::network_unreachable, 0)); - } - - void - testJavadocs() - { - BEAST_EXPECT(( - &bind_handler_test::signal_aborted< - test::stream, handler>)); - - BEAST_EXPECT(( - &bind_handler_test::signal_eof< - test::stream, handler>)); - - BEAST_EXPECT(( - &bind_handler_test::signal_unreachable< - test::stream, handler>)); - } - - //-------------------------------------------------------------------------- - struct copyable { template @@ -307,6 +259,12 @@ public: { std::bind(bind_front_handler(test_cb{*this})); } + + void + failStdBindBack() + { + std::bind(bind_back_handler(test_cb{*this})); + } #endif //-------------------------------------------------------------------------- @@ -440,6 +398,13 @@ public: bind_handler(test_cb{*this}, 42)); ioc.run(); } + + // legacy hooks + legacy_handler::test(*this, + [](legacy_handler h) + { + return bind_handler(h); + }); } void @@ -513,14 +478,165 @@ public: test_executor(*this, ioc), test_cb{*this}), ec, n)); } + + // legacy hooks + legacy_handler::test(*this, + [](legacy_handler h) + { + return bind_front_handler(h); + }); + legacy_handler::test(*this, + [](legacy_handler h) + { + return bind_front_handler( + h, error_code{}, std::size_t{}); + }); } + + void + testBindBackHandler() + { + using m1 = move_arg<1>; + using m2 = move_arg<2>; + + // 0-ary + bind_back_handler(test_cb{*this})(); + + // 1-ary + bind_back_handler(test_cb{*this}, 42)(); + bind_back_handler(test_cb{*this})(42); + + // 2-ary + bind_back_handler(test_cb{*this}, 42, "s")(); + bind_back_handler(test_cb{*this}, "s")(42); + bind_back_handler(test_cb{*this})(42, "s"); + + // 3-ary + bind_back_handler(test_cb{*this}, 42, "s", m1{})(); + bind_back_handler(test_cb{*this}, m1{})(42, "s"); + bind_back_handler(test_cb{*this}, "s", m1{})(42); + bind_back_handler(test_cb{*this})(42, "s", m1{}); + + // 4-ary + bind_back_handler(test_cb{*this}, 42, "s", m1{}, m2{})(); + bind_back_handler(test_cb{*this}, "s", m1{}, m2{})(42); + bind_back_handler(test_cb{*this}, m1{}, m2{})(42, "s"); + bind_back_handler(test_cb{*this}, "s", m1{}, m2{})(42); + bind_back_handler(test_cb{*this})(42, "s", m1{}, m2{}); + + error_code ec; + std::size_t n = 256; + + // void(error_code, size_t) + bind_back_handler(test_cb{*this}, ec, n)(); + + // void(error_code, size_t)(string_view) + bind_back_handler(test_cb{*this}, "s")(ec, n); + + // perfect forwarding + { + std::shared_ptr const sp = + std::make_shared(42); + bind_back_handler(test_cb{*this}, sp)(); + BEAST_EXPECT(sp.get() != nullptr); + } + + // associated executor + { + net::io_context ioc; + + testHooks(ioc, bind_back_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}) + )); + testHooks(ioc, bind_back_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42)); + testHooks(ioc, bind_back_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42, "s")); + testHooks(ioc, bind_back_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42, "s", m1{})); + testHooks(ioc, bind_back_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + 42, "s", m1{}, m2{})); + testHooks(ioc, bind_back_handler(net::bind_executor( + test_executor(*this, ioc), test_cb{*this}), + ec, n)); + } + + // legacy hooks + legacy_handler::test(*this, + [](legacy_handler h) + { + return bind_back_handler(h); + }); + legacy_handler::test(*this, + [](legacy_handler h) + { + return bind_back_handler( + h, error_code{}, std::size_t{}); + }); + } + + //-------------------------------------------------------------------------- + + template + void + signal_aborted (AsyncReadStream& stream, ReadHandler&& handler) + { + net::post( + stream.get_executor(), + bind_handler (std::forward (handler), + net::error::operation_aborted, 0)); + } + + template + void + signal_eof (AsyncReadStream& stream, ReadHandler&& handler) + { + net::post( + stream.get_executor(), + bind_front_handler (std::forward (handler), + net::error::eof, 0)); + } + + template + void + signal_unreachable (AsyncReadStream& stream, ReadHandler&& handler) + { + net::post( + stream.get_executor(), + bind_back_handler (std::forward (handler), + net::error::network_unreachable, 0)); + } + + void + testJavadocs() + { + BEAST_EXPECT(( + &bind_handler_test::signal_aborted< + test::stream, handler>)); + + BEAST_EXPECT(( + &bind_handler_test::signal_eof< + test::stream, handler>)); + + BEAST_EXPECT(( + &bind_handler_test::signal_unreachable< + test::stream, handler>)); + } + + //-------------------------------------------------------------------------- + void run() override { - testJavadocs(); testBindHandler(); testBindFrontHandler(); + testBindBackHandler(); + testJavadocs(); } }; diff --git a/test/beast/core/test_handler.hpp b/test/beast/core/test_handler.hpp new file mode 100644 index 00000000..62170c84 --- /dev/null +++ b/test/beast/core/test_handler.hpp @@ -0,0 +1,129 @@ +// +// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_TEST_HANDLER_HPP +#define BOOST_BEAST_TEST_HANDLER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { + +// Used to test the legacy handler hooks +struct legacy_handler +{ + bool& hook_invoked; + + struct executor + { + void* context() { return nullptr; } + void on_work_started() {} + void on_work_finished() {} + template void dispatch(F&&) {} + template void post(F&&) {} + template void defer(F&&) {} + }; + + executor + get_executor() const noexcept + { + return {}; + }; + + // Signature of `f` is H where H is the wrapper to test + template + static + void + test(unit_test::suite& suite, F const& f) + { + { + bool hook_invoked = false; + bool lambda_invoked = false; + auto h = f(legacy_handler{hook_invoked}); + using net::asio_handler_invoke; + asio_handler_invoke( + [&lambda_invoked] + { + lambda_invoked =true; + }, &h); + suite.BEAST_EXPECT(hook_invoked); + suite.BEAST_EXPECT(lambda_invoked); + } + { + bool hook_invoked = false; + auto h = f(legacy_handler{hook_invoked}); + using net::asio_handler_allocate; + asio_handler_allocate(0, &h); + suite.BEAST_EXPECT(hook_invoked); + } + { + bool hook_invoked = false; + auto h = f(legacy_handler{hook_invoked}); + using net::asio_handler_deallocate; + asio_handler_deallocate(nullptr, 0, &h); + suite.BEAST_EXPECT(hook_invoked); + } + { + bool hook_invoked = false; + auto h = f(legacy_handler{hook_invoked}); + using net::asio_handler_is_continuation; + asio_handler_is_continuation(&h); + suite.BEAST_EXPECT(hook_invoked); + } + } +}; + +template +void +asio_handler_invoke( + Function&& f, + legacy_handler* p) +{ + p->hook_invoked = true; + f(); +} + +inline +void* +asio_handler_allocate( + std::size_t, + legacy_handler* p) +{ + p->hook_invoked = true; + return nullptr; +} + +inline +void +asio_handler_deallocate( + void*, std::size_t, + legacy_handler* p) +{ + p->hook_invoked = true; +} + +inline +bool +asio_handler_is_continuation( + legacy_handler* p) +{ + p->hook_invoked = true; + return false; +} + +} // beast +} // boost + +#endif