diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index 9a7039e..2feaea1 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -404,6 +404,24 @@ public: std::forward(executor)); } + /// Returns a continuable_base which will have its signature converted + /// to the given Args. + /// + /// A signature can only be converted if it can be partially applied + /// from the previous one as shown below: + /// ```cpp + /// continuable c = make_ready_continuable(0, 1, 2).as(); + /// ``` + /// + /// \returns Returns a continuable_base with an asynchronous return type + /// matching the given Args. + /// + /// \since 4.0.0 + template + auto as() && { + return std::move(*this).then(detail::base::convert_to{}); + } + /// A method which allows to apply this continuable to the given callable. /// /// \param transform A transform which shall accept this continuable diff --git a/include/continuable/detail/core/base.hpp b/include/continuable/detail/core/base.hpp index 6d8d9aa..a9a10a1 100644 --- a/include/continuable/detail/core/base.hpp +++ b/include/continuable/detail/core/base.hpp @@ -473,8 +473,10 @@ void on_executor(Executor&& executor, Invoker&& invoker, Args&&... args) { // Create a worker object which when invoked calls the callback with the // the returned arguments. - auto work = [invoker = std::forward(invoker), - args = std::make_tuple(std::forward(args)...)]() mutable { + auto work = [ + invoker = std::forward(invoker), + args = std::make_tuple(std::forward(args)...) + ]() mutable { traits::unpack( [&](auto&&... captured_args) { // Just use the packed dispatch method which dispatches the work on @@ -915,6 +917,25 @@ auto wrap_continuation(Continuation&& continuation) { return supplier_callback>( std::forward(continuation)); } + +/// Callback which converts its input to the given set of arguments +template +struct convert_to { + std::tuple operator()(Args... args) { + return std::make_tuple(std::move(args)...); + } +}; +template +struct convert_to { + T operator()(T arg) noexcept { + return std::move(arg); + } +}; +template <> +struct convert_to<> { + void operator()() noexcept { + } +}; } // namespace base } // namespace detail } // namespace cti diff --git a/test/unit-test/multi/test-continuable-base-chaining.cpp b/test/unit-test/multi/test-continuable-base-chaining.cpp index 06685ee..9206fd9 100644 --- a/test/unit-test/multi/test-continuable-base-chaining.cpp +++ b/test/unit-test/multi/test-continuable-base-chaining.cpp @@ -60,3 +60,27 @@ TYPED_TEST(single_dimension_tests, are_continuing_chainable) { ASSERT_ASYNC_TYPES(std::move(chain), tag1); } + +TYPED_TEST(single_dimension_tests, are_converted_chainable_0) { + auto chain = + this->supply().then([&] { return this->supply(tag1{}).template as<>(); }); + + ASSERT_ASYNC_COMPLETION(std::move(chain).then([](auto&&... args) { + ASSERT_EQ(sizeof...(args), 0U); // + })); +} + +TYPED_TEST(single_dimension_tests, are_converted_chainable_1) { + auto chain = this->supply().then( + [&] { return this->supply(float(0)).template as(); }); + + ASSERT_ASYNC_TYPES(std::move(chain), double); +} + +TYPED_TEST(single_dimension_tests, are_converted_chainable_2) { + auto chain = this->supply().then([&] { + return this->supply(int(0), float(0)).template as(); + }); + + ASSERT_ASYNC_TYPES(std::move(chain), long, double); +}