mirror of
https://github.com/Naios/continuable.git
synced 2025-12-08 01:36:46 +08:00
Make promise_base default constructible
* This makes it possible to use promise_base for optional promises directly rather than wrapping it as optional<promise_base<...>>. * Invalidate the promise_base after its first usage. * Expose an `operator bool()` to make the validility accessible. * Remove the no longer needed private promise_no_init_arg_t tag.
This commit is contained in:
parent
2bc448b905
commit
fdd9a061c4
@ -31,6 +31,7 @@
|
||||
#ifndef CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||
#define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
@ -74,55 +75,118 @@ class promise_base<Data, detail::identity<Args...>>
|
||||
/// \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 <typename OData,
|
||||
std::enable_if_t<std::is_convertible<
|
||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||
promise_base(OData&& data) : data_(std::forward<OData>(data)) {
|
||||
/* implicit */ promise_base(OData&& data) : data_(std::forward<OData>(data)) {
|
||||
}
|
||||
|
||||
/// Assignment operator accepting any object convertible to the data object
|
||||
template <typename OData,
|
||||
std::enable_if_t<std::is_convertible<
|
||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||
promise_base& operator=(OData&& data) {
|
||||
data_ = std::forward<OData>(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_);
|
||||
}
|
||||
};
|
||||
/// \}
|
||||
|
||||
@ -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>(invoker),
|
||||
args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
|
||||
auto work = [
|
||||
invoker = std::forward<Invoker>(invoker),
|
||||
args = std::make_tuple(std::forward<Args>(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<identity<Args...>, 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 <typename Hint, handle_results HandleResults,
|
||||
@ -682,6 +689,10 @@ struct final_callback : util::non_copyable {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
std::move (*this)(exception_arg_t{}, std::move(error));
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace callbacks
|
||||
|
||||
|
||||
@ -76,9 +76,6 @@ public:
|
||||
using T::operator();
|
||||
};
|
||||
|
||||
/// Tag for constructing an empty promise_base .
|
||||
struct promise_no_init_arg_t {};
|
||||
|
||||
/// Marks a given callable object as transformation
|
||||
template <typename T>
|
||||
class plain_tag {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -48,6 +48,7 @@ TYPED_TEST(single_dimension_tests, are_called_on_destruct) {
|
||||
template <typename T>
|
||||
auto create_incomplete(T* me) {
|
||||
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
||||
EXPECT_TRUE(callback);
|
||||
// Destruct the callback here
|
||||
auto destroy = std::forward<decltype(callback)>(callback);
|
||||
(void)destroy;
|
||||
@ -57,6 +58,7 @@ auto create_incomplete(T* me) {
|
||||
template <typename T>
|
||||
auto create_incomplete_cancelling(T* me) {
|
||||
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
||||
EXPECT_TRUE(callback);
|
||||
make_cancelling_continuable<void>().next(
|
||||
std::forward<decltype(callback)>(callback));
|
||||
});
|
||||
@ -64,8 +66,10 @@ auto create_incomplete_cancelling(T* me) {
|
||||
|
||||
template <typename T>
|
||||
auto assert_invocation(T* me) {
|
||||
return me->make(identity<>{}, identity<void>{},
|
||||
[](auto&& /*callback*/) mutable { FAIL(); });
|
||||
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
||||
EXPECT_TRUE(callback);
|
||||
FAIL();
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST(single_dimension_tests, are_incomplete_when_frozen) {
|
||||
|
||||
@ -50,8 +50,9 @@ TYPED_TEST(single_dimension_tests, is_eraseable) {
|
||||
TYPED_TEST(single_dimension_tests, is_callable) {
|
||||
|
||||
cti::continuable<int, int> erased = [](cti::promise<int, int>&& callback) {
|
||||
|
||||
EXPECT_TRUE(callback);
|
||||
std::move(callback)(0xDF, 0xDD);
|
||||
EXPECT_FALSE(callback);
|
||||
};
|
||||
|
||||
EXPECT_ASYNC_RESULT(std::move(erased), 0xDF, 0xDD);
|
||||
|
||||
47
test/unit-test/single/test-continuable-promise.cpp
Normal file
47
test/unit-test/single/test-continuable-promise.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
/*
|
||||
Copyright(c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 <test-continuable.hpp>
|
||||
|
||||
using namespace cti;
|
||||
|
||||
TEST(promise_tests, are_invalidated) {
|
||||
ASSERT_ASYNC_COMPLETION(make_continuable<void>([](promise<> promise) {
|
||||
EXPECT_TRUE(promise);
|
||||
promise.set_value();
|
||||
EXPECT_FALSE(promise);
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(promise_tests, are_move_assignable) {
|
||||
ASSERT_ASYNC_COMPLETION(make_continuable<void>([](auto&& initial) {
|
||||
promise<> other;
|
||||
EXPECT_FALSE(other);
|
||||
EXPECT_TRUE(initial);
|
||||
|
||||
other = std::forward<decltype(initial)>(initial);
|
||||
EXPECT_TRUE(other);
|
||||
other.set_value();
|
||||
EXPECT_FALSE(other);
|
||||
}));
|
||||
}
|
||||
@ -49,6 +49,7 @@ template <typename... Args>
|
||||
auto supplier_of(Args&&... args) {
|
||||
return [values = std::make_tuple(std::forward<Args>(args)...)](
|
||||
auto&& promise) mutable {
|
||||
EXPECT_TRUE(promise);
|
||||
cti::detail::traits::unpack(
|
||||
[&](auto&&... passed) {
|
||||
promise.set_value(std::forward<decltype(passed)>(passed)...);
|
||||
@ -71,6 +72,7 @@ public:
|
||||
auto invoke(T&& type) {
|
||||
return this->make(identity<>{}, identity<void>{},
|
||||
[type = std::forward<T>(type)](auto&& promise) mutable {
|
||||
EXPECT_TRUE(promise);
|
||||
promise.set_value();
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user