diff --git a/include/continuable/detail/composition.hpp b/include/continuable/detail/composition.hpp index a48fc9d..6ff8bee 100644 --- a/include/continuable/detail/composition.hpp +++ b/include/continuable/detail/composition.hpp @@ -41,6 +41,7 @@ #include #include #include +#include namespace cti { namespace detail { @@ -111,6 +112,7 @@ constexpr void assign(traits::size_constant /*pos*/, T& /*storage*/) { template void assign(traits::size_constant pos, T& storage, Current&& current, Args&&... args) { + // TODO Improve this -> linear instantiation std::get(storage) = std::forward(current); assign(pos + traits::size_constant_of<1>(), storage, std::forward(args)...); @@ -125,6 +127,7 @@ class all_result_submitter : public std::enable_shared_from_this< T callback_; std::atomic left_; + std::once_flag flag_; std::tuple result_; public: @@ -134,30 +137,50 @@ public: /// Creates a submitter which submits it's result into the tuple template - auto create_callback(traits::size_constant from, + auto create_callback(traits::size_constant /*from*/, traits::size_constant /*to*/) { - return [ me = this->shared_from_this(), from ](auto&&... args) { - static_assert(sizeof...(args) == (To - From), - "Submission called with the wrong amount of arguments!"); - - // Assign the values from the result to it's correct positions of the - // tuple. Maybe think about the thread safety again...: - // http://stackoverflow.com/questions/40845699 - assign(from, me->result_, std::forward(args)...); - - // Complete the current result - me->complete_one(); + return [me = this->shared_from_this()](auto&&... args) { + // Resolve the and composition with the given arguments at the + // stored position + me->resolve(traits::size_constant{}, traits::size_constant{}, + std::forward(args)...); }; } private: + template + void resolve(traits::size_constant from, traits::size_constant, + PartialArgs&&... args) { + + static_assert(sizeof...(args) == (To - From), + "Submission called with the wrong amount of arguments!"); + + // Assign the values from the result to it's correct positions of the + // tuple. Maybe think about the thread safety again...: + // http://stackoverflow.com/questions/40845699 + assign(from, result_, std::forward(args)...); + + // Complete the current result + complete_one(); + } + + template + void resolve(traits::size_constant, traits::size_constant, + types::dispatch_error_tag tag, types::error_type error) { + + // We never complete the composition, but we forward the first error + // which was raised. + std::call_once(flag_, std::move(callback_), tag, std::move(error)); + } + // Invokes the callback with the cached result void invoke() { assert((left_ == 0U) && "Expected that the submitter is finished!"); std::atomic_thread_fence(std::memory_order_acquire); traits::unpack(std::move(result_), [&](auto&&... args) { - std::move(callback_)(std::forward(args)...); + std::call_once(flag_, std::move(callback_), + std::forward(args)...); }); } // Completes one result diff --git a/test/playground/test-playground.cpp b/test/playground/test-playground.cpp index 324d685..e15ee6d 100644 --- a/test/playground/test-playground.cpp +++ b/test/playground/test-playground.cpp @@ -69,7 +69,7 @@ int main(int, char**) { .fail([](std::error_condition) { // ... }) - .then([](int) { + .then([] { // ... }); @@ -78,10 +78,26 @@ int main(int, char**) { // ... return 0; }) - .fail([](std::error_condition) { + .then([](int) { // ... }) - .then([](int) { + .fail([](std::error_condition) { + // ... + }); + + (http_request("github.com") && http_request("github.com")) + .then([](std::string, std::string) { + // ... + }) + .fail([](std::error_condition) { + // ... + }); + + (http_request("github.com") || http_request("github.com")) + .then([](std::string) { + // ... + }) + .fail([](std::error_condition) { // ... });