mirror of
https://github.com/Naios/continuable.git
synced 2026-02-08 18:56:40 +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
|
#ifndef CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||||
#define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
#define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <continuable/continuable-primitives.hpp>
|
#include <continuable/continuable-primitives.hpp>
|
||||||
@ -74,55 +75,118 @@ class promise_base<Data, detail::identity<Args...>>
|
|||||||
/// \endcond
|
/// \endcond
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// Constructor for constructing an empty promise
|
||||||
|
explicit promise_base() = default;
|
||||||
/// Constructor accepting the data object
|
/// Constructor accepting the data object
|
||||||
explicit promise_base(Data data) : data_(std::move(data)) {
|
explicit promise_base(Data data) : data_(std::move(data)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \cond false
|
/// \cond false
|
||||||
/// Constructor for constructing an empty promise
|
promise_base(promise_base&&) = default;
|
||||||
explicit promise_base(detail::types::promise_no_init_arg_t) {
|
promise_base(promise_base const&) = delete;
|
||||||
}
|
|
||||||
|
promise_base& operator=(promise_base&&) = default;
|
||||||
|
promise_base& operator=(promise_base const&) = delete;
|
||||||
/// \endcond
|
/// \endcond
|
||||||
|
|
||||||
/// Constructor accepting any object convertible to the data object
|
/// Constructor accepting any object convertible to the data object
|
||||||
template <typename OData,
|
template <typename OData,
|
||||||
std::enable_if_t<std::is_convertible<
|
std::enable_if_t<std::is_convertible<
|
||||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
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.
|
/// Resolves the continuation with the given values.
|
||||||
///
|
///
|
||||||
/// \throws This method never throws an 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
|
/// \since 2.0.0
|
||||||
void operator()(Args... args) && noexcept {
|
void operator()(Args... args) && noexcept {
|
||||||
|
assert(data_);
|
||||||
std::move(data_)(std::move(args)...);
|
std::move(data_)(std::move(args)...);
|
||||||
|
data_ = nullptr;
|
||||||
}
|
}
|
||||||
/// Resolves the continuation with the given exception.
|
/// Resolves the continuation with the given exception.
|
||||||
///
|
///
|
||||||
/// \throws This method never throws an 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
|
/// \since 2.0.0
|
||||||
void operator()(exception_arg_t tag, exception_t exception) && noexcept {
|
void operator()(exception_arg_t tag, exception_t exception) && noexcept {
|
||||||
|
assert(data_);
|
||||||
std::move(data_)(tag, std::move(exception));
|
std::move(data_)(tag, std::move(exception));
|
||||||
|
data_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the continuation with the given values.
|
/// Resolves the continuation with the given values.
|
||||||
///
|
///
|
||||||
/// \throws This method never throws an 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
|
/// \since 2.0.0
|
||||||
void set_value(Args... args) noexcept {
|
void set_value(Args... args) noexcept {
|
||||||
|
// assert(data_);
|
||||||
std::move(data_)(std::move(args)...);
|
std::move(data_)(std::move(args)...);
|
||||||
|
data_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the continuation with the given exception.
|
/// Resolves the continuation with the given exception.
|
||||||
///
|
///
|
||||||
/// \throws This method never throws an 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
|
/// \since 2.0.0
|
||||||
void set_exception(exception_t exception) noexcept {
|
void set_exception(exception_t exception) noexcept {
|
||||||
|
assert(data_);
|
||||||
std::move(data_)(exception_arg_t{}, std::move(exception));
|
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
|
// Create a worker object which when invoked calls the callback with the
|
||||||
// the returned arguments.
|
// the returned arguments.
|
||||||
auto work = [invoker = std::forward<Invoker>(invoker),
|
auto work = [
|
||||||
args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
|
invoker = std::forward<Invoker>(invoker),
|
||||||
|
args = std::make_tuple(std::forward<Args>(args)...)
|
||||||
|
]() mutable {
|
||||||
traits::unpack(
|
traits::unpack(
|
||||||
[&](auto&&... captured_args) {
|
[&](auto&&... captured_args) {
|
||||||
// Just use the packed dispatch method which dispatches the work on
|
// 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) {
|
void set_exception(exception_t error) {
|
||||||
std::move (*this)(exception_arg_t{}, std::move(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,
|
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)
|
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||||
std::move (*this)(exception_arg_t{}, std::move(error));
|
std::move (*this)(exception_arg_t{}, std::move(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace callbacks
|
} // namespace callbacks
|
||||||
|
|
||||||
|
|||||||
@ -76,9 +76,6 @@ public:
|
|||||||
using T::operator();
|
using T::operator();
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Tag for constructing an empty promise_base .
|
|
||||||
struct promise_no_init_arg_t {};
|
|
||||||
|
|
||||||
/// Marks a given callable object as transformation
|
/// Marks a given callable object as transformation
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class plain_tag {
|
class plain_tag {
|
||||||
|
|||||||
@ -200,8 +200,7 @@ struct promise_type
|
|||||||
coroutine_handle<> handle_;
|
coroutine_handle<> handle_;
|
||||||
Promise promise_;
|
Promise promise_;
|
||||||
|
|
||||||
explicit promise_type() : promise_(types::promise_no_init_arg_t{}) {
|
explicit promise_type() = default;
|
||||||
}
|
|
||||||
|
|
||||||
Continuable get_return_object() {
|
Continuable get_return_object() {
|
||||||
return [this](auto&& promise) {
|
return [this](auto&& promise) {
|
||||||
|
|||||||
@ -94,6 +94,10 @@ public:
|
|||||||
void operator()(exception_arg_t exception_arg, exception_t exception) && {
|
void operator()(exception_arg_t exception_arg, exception_t exception) && {
|
||||||
std::move(erasure_)(exception_arg, std::move(exception));
|
std::move(erasure_)(exception_arg, std::move(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept {
|
||||||
|
return bool(erasure_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ target_link_libraries(test-continuable-base
|
|||||||
continuable-features-noexcept)
|
continuable-features-noexcept)
|
||||||
|
|
||||||
add_executable(test-continuable-single
|
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-connection-noinst
|
||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-forward-decl.cpp
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-forward-decl.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-flat-variant.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>
|
template <typename T>
|
||||||
auto create_incomplete(T* me) {
|
auto create_incomplete(T* me) {
|
||||||
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
||||||
|
EXPECT_TRUE(callback);
|
||||||
// Destruct the callback here
|
// Destruct the callback here
|
||||||
auto destroy = std::forward<decltype(callback)>(callback);
|
auto destroy = std::forward<decltype(callback)>(callback);
|
||||||
(void)destroy;
|
(void)destroy;
|
||||||
@ -57,6 +58,7 @@ auto create_incomplete(T* me) {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
auto create_incomplete_cancelling(T* me) {
|
auto create_incomplete_cancelling(T* me) {
|
||||||
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
||||||
|
EXPECT_TRUE(callback);
|
||||||
make_cancelling_continuable<void>().next(
|
make_cancelling_continuable<void>().next(
|
||||||
std::forward<decltype(callback)>(callback));
|
std::forward<decltype(callback)>(callback));
|
||||||
});
|
});
|
||||||
@ -64,8 +66,10 @@ auto create_incomplete_cancelling(T* me) {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto assert_invocation(T* me) {
|
auto assert_invocation(T* me) {
|
||||||
return me->make(identity<>{}, identity<void>{},
|
return me->make(identity<>{}, identity<void>{}, [](auto&& callback) mutable {
|
||||||
[](auto&& /*callback*/) mutable { FAIL(); });
|
EXPECT_TRUE(callback);
|
||||||
|
FAIL();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TYPED_TEST(single_dimension_tests, are_incomplete_when_frozen) {
|
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) {
|
TYPED_TEST(single_dimension_tests, is_callable) {
|
||||||
|
|
||||||
cti::continuable<int, int> erased = [](cti::promise<int, int>&& callback) {
|
cti::continuable<int, int> erased = [](cti::promise<int, int>&& callback) {
|
||||||
|
EXPECT_TRUE(callback);
|
||||||
std::move(callback)(0xDF, 0xDD);
|
std::move(callback)(0xDF, 0xDD);
|
||||||
|
EXPECT_FALSE(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
EXPECT_ASYNC_RESULT(std::move(erased), 0xDF, 0xDD);
|
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) {
|
auto supplier_of(Args&&... args) {
|
||||||
return [values = std::make_tuple(std::forward<Args>(args)...)](
|
return [values = std::make_tuple(std::forward<Args>(args)...)](
|
||||||
auto&& promise) mutable {
|
auto&& promise) mutable {
|
||||||
|
EXPECT_TRUE(promise);
|
||||||
cti::detail::traits::unpack(
|
cti::detail::traits::unpack(
|
||||||
[&](auto&&... passed) {
|
[&](auto&&... passed) {
|
||||||
promise.set_value(std::forward<decltype(passed)>(passed)...);
|
promise.set_value(std::forward<decltype(passed)>(passed)...);
|
||||||
@ -71,6 +72,7 @@ public:
|
|||||||
auto invoke(T&& type) {
|
auto invoke(T&& type) {
|
||||||
return this->make(identity<>{}, identity<void>{},
|
return this->make(identity<>{}, identity<void>{},
|
||||||
[type = std::forward<T>(type)](auto&& promise) mutable {
|
[type = std::forward<T>(type)](auto&& promise) mutable {
|
||||||
|
EXPECT_TRUE(promise);
|
||||||
promise.set_value();
|
promise.set_value();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user