diff --git a/include/continuable/continuable-promise-base.hpp b/include/continuable/continuable-promise-base.hpp index 432f201..c344f43 100644 --- a/include/continuable/continuable-promise-base.hpp +++ b/include/continuable/continuable-promise-base.hpp @@ -31,6 +31,7 @@ #ifndef CONTINUABLE_PROMISE_BASE_HPP_INCLUDED #define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED +#include #include #include #include @@ -74,55 +75,118 @@ class promise_base> /// \endcond public: + /// Constructor for constructing an empty promise + explicit promise_base() = default; /// Constructor accepting the data object explicit promise_base(Data data) : data_(std::move(data)) { } + /// \cond false - /// Constructor for constructing an empty promise - explicit promise_base(detail::types::promise_no_init_arg_t) { - } + promise_base(promise_base&&) = default; + promise_base(promise_base const&) = delete; + + promise_base& operator=(promise_base&&) = default; + promise_base& operator=(promise_base const&) = delete; /// \endcond /// Constructor accepting any object convertible to the data object template , Data>::value>* = nullptr> - promise_base(OData&& data) : data_(std::forward(data)) { + /* implicit */ promise_base(OData&& data) : data_(std::forward(data)) { + } + + /// Assignment operator accepting any object convertible to the data object + template , Data>::value>* = nullptr> + promise_base& operator=(OData&& data) { + data_ = std::forward(data); + return *this; } /// Resolves the continuation with the given values. /// /// \throws This method never throws an exception. /// + /// \attention This method may only be called once, + /// when the promise is valid operator bool() returns true. + /// Calling this method will invalidate the promise such that + /// subsequent calls to operator bool() will return false. + /// This behaviour is only consistent in promise_base and + /// non type erased promises may behave differently. + /// Invoking an invalid promise_base is undefined! + /// /// \since 2.0.0 void operator()(Args... args) && noexcept { + assert(data_); std::move(data_)(std::move(args)...); + data_ = nullptr; } /// Resolves the continuation with the given exception. /// /// \throws This method never throws an exception. /// + /// \attention This method may only be called once, + /// when the promise is valid operator bool() returns true. + /// Calling this method will invalidate the promise such that + /// subsequent calls to operator bool() will return false. + /// This behaviour is only consistent in promise_base and + /// non type erased promises may behave differently. + /// Invoking an invalid promise_base is undefined! + /// /// \since 2.0.0 void operator()(exception_arg_t tag, exception_t exception) && noexcept { + assert(data_); std::move(data_)(tag, std::move(exception)); + data_ = nullptr; } /// Resolves the continuation with the given values. /// /// \throws This method never throws an exception. /// + /// \attention This method may only be called once, + /// when the promise is valid operator bool() returns true. + /// Calling this method will invalidate the promise such that + /// subsequent calls to operator bool() will return false. + /// This behaviour is only consistent in promise_base and + /// non type erased promises may behave differently. + /// Invoking an invalid promise_base is undefined! + /// /// \since 2.0.0 void set_value(Args... args) noexcept { + // assert(data_); std::move(data_)(std::move(args)...); + data_ = nullptr; } /// Resolves the continuation with the given exception. /// /// \throws This method never throws an exception. /// + /// \attention This method may only be called once, + /// when the promise is valid operator bool() returns true. + /// Calling this method will invalidate the promise such that + /// subsequent calls to operator bool() will return false. + /// This behaviour is only consistent in promise_base and + /// non type erased promises may behave differently. + /// Invoking an invalid promise_base is undefined! + /// /// \since 2.0.0 void set_exception(exception_t exception) noexcept { + assert(data_); std::move(data_)(exception_arg_t{}, std::move(exception)); + data_ = nullptr; + } + + /// Returns true if the continuation is valid (non empty). + /// + /// \throws This method never throws an exception. + /// + /// \since 4.0.0 + explicit operator bool() const noexcept { + return bool(data_); } }; /// \} diff --git a/include/continuable/detail/core/base.hpp b/include/continuable/detail/core/base.hpp index c1814f2..96313c9 100644 --- a/include/continuable/detail/core/base.hpp +++ b/include/continuable/detail/core/base.hpp @@ -486,8 +486,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 @@ -633,6 +635,11 @@ struct callback_base, HandleResults, HandleErrors, Callback, void set_exception(exception_t error) { std::move (*this)(exception_arg_t{}, std::move(error)); } + + /// Returns true because this is a present continuation + explicit operator bool() const noexcept { + return true; + } }; template class plain_tag { diff --git a/include/continuable/detail/other/coroutines.hpp b/include/continuable/detail/other/coroutines.hpp index 5a53bfe..c7d0a51 100644 --- a/include/continuable/detail/other/coroutines.hpp +++ b/include/continuable/detail/other/coroutines.hpp @@ -200,8 +200,7 @@ struct promise_type coroutine_handle<> handle_; Promise promise_; - explicit promise_type() : promise_(types::promise_no_init_arg_t{}) { - } + explicit promise_type() = default; Continuable get_return_object() { return [this](auto&& promise) { diff --git a/include/continuable/detail/other/erasure.hpp b/include/continuable/detail/other/erasure.hpp index 474fddb..a1bbe79 100644 --- a/include/continuable/detail/other/erasure.hpp +++ b/include/continuable/detail/other/erasure.hpp @@ -94,6 +94,10 @@ public: void operator()(exception_arg_t exception_arg, exception_t exception) && { std::move(erasure_)(exception_arg, std::move(exception)); } + + explicit operator bool() const noexcept { + return bool(erasure_); + } }; #endif diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index 9e582ec..ed28b26 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -16,6 +16,7 @@ target_link_libraries(test-continuable-base continuable-features-noexcept) add_executable(test-continuable-single + ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-promise.cpp ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-connection-noinst ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-forward-decl.cpp ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-flat-variant.cpp diff --git a/test/unit-test/multi/test-continuable-base-destruct.cpp b/test/unit-test/multi/test-continuable-base-destruct.cpp index 6017998..c0cc1c0 100644 --- a/test/unit-test/multi/test-continuable-base-destruct.cpp +++ b/test/unit-test/multi/test-continuable-base-destruct.cpp @@ -48,6 +48,7 @@ TYPED_TEST(single_dimension_tests, are_called_on_destruct) { template auto create_incomplete(T* me) { return me->make(identity<>{}, identity{}, [](auto&& callback) mutable { + EXPECT_TRUE(callback); // Destruct the callback here auto destroy = std::forward(callback); (void)destroy; @@ -57,6 +58,7 @@ auto create_incomplete(T* me) { template auto create_incomplete_cancelling(T* me) { return me->make(identity<>{}, identity{}, [](auto&& callback) mutable { + EXPECT_TRUE(callback); make_cancelling_continuable().next( std::forward(callback)); }); @@ -64,8 +66,10 @@ auto create_incomplete_cancelling(T* me) { template auto assert_invocation(T* me) { - return me->make(identity<>{}, identity{}, - [](auto&& /*callback*/) mutable { FAIL(); }); + return me->make(identity<>{}, identity{}, [](auto&& callback) mutable { + EXPECT_TRUE(callback); + FAIL(); + }); } TYPED_TEST(single_dimension_tests, are_incomplete_when_frozen) { diff --git a/test/unit-test/multi/test-continuable-erasure.cpp b/test/unit-test/multi/test-continuable-erasure.cpp index bb4fb4e..e7862b1 100644 --- a/test/unit-test/multi/test-continuable-erasure.cpp +++ b/test/unit-test/multi/test-continuable-erasure.cpp @@ -50,8 +50,9 @@ TYPED_TEST(single_dimension_tests, is_eraseable) { TYPED_TEST(single_dimension_tests, is_callable) { cti::continuable erased = [](cti::promise&& callback) { - + EXPECT_TRUE(callback); std::move(callback)(0xDF, 0xDD); + EXPECT_FALSE(callback); }; EXPECT_ASYNC_RESULT(std::move(erased), 0xDF, 0xDD); diff --git a/test/unit-test/single/test-continuable-promise.cpp b/test/unit-test/single/test-continuable-promise.cpp new file mode 100644 index 0000000..7a4e684 --- /dev/null +++ b/test/unit-test/single/test-continuable-promise.cpp @@ -0,0 +1,47 @@ + +/* + Copyright(c) 2015 - 2019 Denis Blank + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files(the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +**/ + +#include + +using namespace cti; + +TEST(promise_tests, are_invalidated) { + ASSERT_ASYNC_COMPLETION(make_continuable([](promise<> promise) { + EXPECT_TRUE(promise); + promise.set_value(); + EXPECT_FALSE(promise); + })); +} + +TEST(promise_tests, are_move_assignable) { + ASSERT_ASYNC_COMPLETION(make_continuable([](auto&& initial) { + promise<> other; + EXPECT_FALSE(other); + EXPECT_TRUE(initial); + + other = std::forward(initial); + EXPECT_TRUE(other); + other.set_value(); + EXPECT_FALSE(other); + })); +} diff --git a/test/unit-test/test-continuable.hpp b/test/unit-test/test-continuable.hpp index e0371da..113ba0a 100644 --- a/test/unit-test/test-continuable.hpp +++ b/test/unit-test/test-continuable.hpp @@ -49,6 +49,7 @@ template auto supplier_of(Args&&... args) { return [values = std::make_tuple(std::forward(args)...)]( auto&& promise) mutable { + EXPECT_TRUE(promise); cti::detail::traits::unpack( [&](auto&&... passed) { promise.set_value(std::forward(passed)...); @@ -71,6 +72,7 @@ public: auto invoke(T&& type) { return this->make(identity<>{}, identity{}, [type = std::forward(type)](auto&& promise) mutable { + EXPECT_TRUE(promise); promise.set_value(); }); }