mirror of
https://github.com/Naios/continuable.git
synced 2025-12-08 01:36:46 +08:00
Rework the expected_trait
* Add tests for the new expected public interface
This commit is contained in:
parent
c76fe9e973
commit
867ab38b8e
@ -35,6 +35,7 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-expected.hpp>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/connection/connection-all.hpp>
|
||||
#include <continuable/detail/connection/connection-any.hpp>
|
||||
@ -898,15 +899,15 @@ constexpr auto make_exceptional_continuable(Exception&& exception) {
|
||||
|
||||
template <typename... Args>
|
||||
auto recover(Args&&... args) {
|
||||
// TODO
|
||||
return make_expected(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
inline auto rethrow(exception_t exception) {
|
||||
// TODO
|
||||
return make_exceptional_expected(std::move(exception));
|
||||
}
|
||||
|
||||
inline constexpr auto cancel() {
|
||||
// TODO
|
||||
inline auto cancel() {
|
||||
return make_none_expected();
|
||||
}
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
@ -34,14 +34,71 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/utility/expected-traits.hpp>
|
||||
#include <continuable/detail/utility/flat-variant.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// A class which is convertible to any expected and that definitly holds no
|
||||
/// value so the real expected gets invalidated when
|
||||
/// this object is passed to it
|
||||
struct empty_expected {
|
||||
empty_expected() = default;
|
||||
empty_expected(empty_expected const&) = default;
|
||||
empty_expected(empty_expected&&) = default;
|
||||
empty_expected& operator=(empty_expected const&) = default;
|
||||
empty_expected& operator=(empty_expected&&) = default;
|
||||
~empty_expected() = default;
|
||||
};
|
||||
|
||||
/// A class which is convertible to any expected and that definitly holds
|
||||
/// an exception which is then passed to the converted expected object.
|
||||
class exceptional_expected {
|
||||
exception_t exception_;
|
||||
|
||||
public:
|
||||
exceptional_expected() = delete;
|
||||
exceptional_expected(exceptional_expected const&) = default;
|
||||
exceptional_expected(exceptional_expected&&) = default;
|
||||
exceptional_expected& operator=(exceptional_expected const&) = default;
|
||||
exceptional_expected& operator=(exceptional_expected&&) = default;
|
||||
~exceptional_expected() = default;
|
||||
|
||||
explicit exceptional_expected(exception_t exception)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
: exception_(std::move(exception)) {
|
||||
}
|
||||
|
||||
exceptional_expected& operator=(exception_t exception) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
exception_ = std::move(exception);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_exception(exception_t exception) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
exception_ = std::move(exception);
|
||||
}
|
||||
|
||||
exception_t& get_exception() & noexcept {
|
||||
return exception_;
|
||||
}
|
||||
exception_t const& get_exception() const& noexcept {
|
||||
return exception_;
|
||||
}
|
||||
exception_t&& get_exception() && noexcept {
|
||||
return std::move(exception_);
|
||||
}
|
||||
};
|
||||
|
||||
/// A class similar to the one in the expected proposal,
|
||||
/// however it's capable of carrying an exception_t.
|
||||
template <typename T>
|
||||
template <typename... T>
|
||||
class expected {
|
||||
detail::container::flat_variant<T, exception_t> variant_;
|
||||
using trait = detail::expected_trait<T...>;
|
||||
using value_t = typename trait::value_t;
|
||||
|
||||
detail::container::flat_variant<value_t, exception_t> variant_;
|
||||
|
||||
public:
|
||||
explicit expected() = default;
|
||||
@ -51,32 +108,39 @@ public:
|
||||
expected& operator=(expected&&) = default;
|
||||
~expected() = default;
|
||||
|
||||
explicit expected(T value) : variant_(std::move(value)) {
|
||||
explicit expected(T... values) : variant_(trait::wrap(std::move(values)...)) {
|
||||
}
|
||||
explicit expected(exception_t exception) : variant_(std::move(exception)) {
|
||||
}
|
||||
explicit expected(empty_expected){};
|
||||
explicit expected(exceptional_expected exceptional_expected)
|
||||
: variant_(std::move(exceptional_expected.get_exception())) {
|
||||
}
|
||||
|
||||
expected& operator=(T value) {
|
||||
variant_ = std::move(value);
|
||||
expected& operator=(empty_expected) {
|
||||
set_empty();
|
||||
return *this;
|
||||
}
|
||||
expected& operator=(exception_t exception) {
|
||||
variant_ = std::move(exception);
|
||||
expected& operator=(exceptional_expected exceptional_expected) {
|
||||
set_exception(std::move(exceptional_expected.get_exception()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_value(T value) {
|
||||
variant_ = std::move(value);
|
||||
void set_empty() {
|
||||
variant_.set_empty();
|
||||
}
|
||||
void set_value(T... values) {
|
||||
variant_ = std::move(values...);
|
||||
}
|
||||
void set_exception(exception_t exception) {
|
||||
variant_ = std::move(exception);
|
||||
}
|
||||
|
||||
bool is_empty() {
|
||||
bool is_empty() const noexcept {
|
||||
return variant_.is_empty();
|
||||
}
|
||||
bool is_value() const noexcept {
|
||||
return variant_.template is<T>();
|
||||
return variant_.template is<value_t>();
|
||||
}
|
||||
bool is_exception() const noexcept {
|
||||
return variant_.template is<exception_t>();
|
||||
@ -86,26 +150,49 @@ public:
|
||||
return is_value();
|
||||
}
|
||||
|
||||
T& get_value() noexcept {
|
||||
return variant_.template cast<T>();
|
||||
value_t& get_value() & noexcept {
|
||||
return variant_.template cast<value_t>();
|
||||
}
|
||||
T const& get_value() const noexcept {
|
||||
return variant_.template cast<T>();
|
||||
value_t const& get_value() const& noexcept {
|
||||
return variant_.template cast<value_t>();
|
||||
}
|
||||
exception_t& get_exception() noexcept {
|
||||
value_t&& get_value() && noexcept {
|
||||
return std::move(variant_).template cast<value_t>();
|
||||
}
|
||||
exception_t& get_exception() & noexcept {
|
||||
return variant_.template cast<exception_t>();
|
||||
}
|
||||
exception_t const& get_exception() const noexcept {
|
||||
exception_t const& get_exception() const& noexcept {
|
||||
return variant_.template cast<exception_t>();
|
||||
}
|
||||
exception_t&& get_exception() && noexcept {
|
||||
return std::move(variant_).template cast<exception_t>();
|
||||
}
|
||||
|
||||
T& operator*() noexcept {
|
||||
value_t& operator*() & noexcept {
|
||||
return get_value();
|
||||
}
|
||||
T const& operator*() const noexcept {
|
||||
value_t const& operator*() const& noexcept {
|
||||
return get_value();
|
||||
}
|
||||
value_t&& operator*() && noexcept {
|
||||
return std::move(*this).get_value();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
constexpr auto make_expected(T&&... values) {
|
||||
return expected<detail::traits::unrefcv_t<T>...>(std::forward<T>(values));
|
||||
}
|
||||
|
||||
inline auto make_exceptional_expected(exception_t exception) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
return exceptional_expected(std::move(exception));
|
||||
}
|
||||
|
||||
inline auto make_empty_expected() {
|
||||
return empty_expected{};
|
||||
}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_EXPECTED_HPP_INCLUDED
|
||||
|
||||
@ -342,7 +342,7 @@ inline auto make_error_invoker(
|
||||
std::integral_constant<handle_errors, handle_errors::plain>) noexcept {
|
||||
return [](auto&& callback, exception_t&& error) {
|
||||
// Errors are not partial invoked
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
std::forward<decltype(callback)>(callback)(std::move(error));
|
||||
};
|
||||
}
|
||||
@ -352,7 +352,8 @@ inline auto make_error_invoker(
|
||||
// Errors are not partial invoked
|
||||
std::forward<decltype(callback)>(callback)(
|
||||
exception_arg_t{},
|
||||
std::move(error)); // NOLINT(hicpp-move-const-arg)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
std::move(error));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,9 @@ using std::experimental::coroutine_handle;
|
||||
/// for waiting on a continuable in a stackless coroutine.
|
||||
template <typename Continuable>
|
||||
class awaitable {
|
||||
using trait_t = container::expected_result_trait_t<Continuable>;
|
||||
using hint_t = decltype(hints::hint_of(traits::identify<Continuable>{}));
|
||||
using trait_t = expected_trait<hint_t>;
|
||||
using value_t = expected_trait<hint_t>;
|
||||
|
||||
/// The continuable which is invoked upon suspension
|
||||
Continuable continuable_;
|
||||
|
||||
@ -31,63 +31,41 @@
|
||||
#ifndef CONTINUABLE_DETAIL_EXPECTED_TRAITS_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_EXPECTED_TRAITS_HPP_INCLUDED
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-expected.hpp>
|
||||
#include <continuable/detail/core/hints.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace container {
|
||||
namespace detail {
|
||||
struct void_guard_tag {};
|
||||
|
||||
template <typename T>
|
||||
struct expected_result_trait;
|
||||
template <typename... T>
|
||||
struct expected_trait;
|
||||
template <>
|
||||
struct expected_result_trait<traits::identity<>> {
|
||||
using expected_type = expected<void_guard_tag>;
|
||||
struct expected_trait<traits::identity<>> {
|
||||
struct value_t {};
|
||||
|
||||
static constexpr void_guard_tag wrap() noexcept {
|
||||
static constexpr value_t wrap() noexcept {
|
||||
return {};
|
||||
}
|
||||
static void unwrap(expected_type&& e) {
|
||||
assert(e.is_value());
|
||||
(void)e;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct expected_result_trait<traits::identity<T>> {
|
||||
using expected_type = expected<T>;
|
||||
struct expected_trait<T> {
|
||||
using value_t = T;
|
||||
|
||||
static auto wrap(T arg) {
|
||||
return std::move(arg);
|
||||
}
|
||||
static auto unwrap(expected_type&& e) {
|
||||
assert(e.is_value());
|
||||
return std::move(e.get_value());
|
||||
}
|
||||
};
|
||||
template <typename First, typename Second, typename... Rest>
|
||||
struct expected_result_trait<traits::identity<First, Second, Rest...>> {
|
||||
using expected_type = expected<std::tuple<First, Second, Rest...>>;
|
||||
struct expected_trait<First, Second, Rest...> {
|
||||
using value_t = std::tuple<First, Second, Rest...>;
|
||||
|
||||
static auto wrap(First first, Second second, Rest... rest) {
|
||||
return std::make_tuple(std::move(first), std::move(second),
|
||||
std::move(rest)...);
|
||||
}
|
||||
static auto unwrap(expected_type&& e) {
|
||||
assert(e.is_value());
|
||||
return std::move(e.get_value());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Continuable>
|
||||
using expected_result_trait_t = detail::expected_result_trait<decltype(
|
||||
hints::hint_of(traits::identify<Continuable>{}))>;
|
||||
} // namespace container
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_EXPECTED_TRAITS_HPP_INCLUDED
|
||||
|
||||
@ -267,6 +267,11 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set_empty() {
|
||||
weak_destroy();
|
||||
set_slot(detail::empty_slot::value);
|
||||
}
|
||||
|
||||
template <typename V, std::size_t Index =
|
||||
traits::index_of_t<std::decay_t<V>, T...>::value>
|
||||
bool is() const noexcept {
|
||||
@ -282,17 +287,24 @@ public:
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
V& cast() noexcept {
|
||||
V& cast() & noexcept {
|
||||
assert(is_slot(traits::index_of_t<std::decay_t<V>, T...>::value));
|
||||
return *reinterpret_cast<std::decay_t<V>*>(&this->storage_);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
V const& cast() const noexcept {
|
||||
V const& cast() const& noexcept {
|
||||
assert(is_slot(traits::index_of_t<std::decay_t<V>, T...>::value));
|
||||
return *reinterpret_cast<std::decay_t<V> const*>(&this->storage_);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
V&& cast() && noexcept {
|
||||
assert(is_slot(traits::index_of_t<std::decay_t<V>, T...>::value));
|
||||
auto& value = *reinterpret_cast<std::decay_t<V> const*>(&this->storage_);
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename C, typename V>
|
||||
static void visit_dispatch(flat_variant* me, V&& visitor) {
|
||||
|
||||
@ -51,6 +51,7 @@ foreach(STEP RANGE ${STEP_RANGE})
|
||||
${CMAKE_CURRENT_LIST_DIR}/multi/test-continuable-base-destruct.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/multi/test-continuable-base-errors.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/multi/test-continuable-base-partial.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/multi/test-continuable-base-multipath.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/multi/test-continuable-connection-all-seq-ag-1.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/multi/test-continuable-connection-all-seq-ag-2.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/multi/test-continuable-connection-all-seq-op.cpp
|
||||
|
||||
30
test/unit-test/multi/test-continuable-base-multipath.cpp
Normal file
30
test/unit-test/multi/test-continuable-base-multipath.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
/*
|
||||
Copyright(c) 2015 - 2018 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>
|
||||
|
||||
TYPED_TEST(single_dimension_tests, are_recoverable) {
|
||||
EXPECT_ASYNC_RESULT(this->supply().then([] () -> cti::expected<> {
|
||||
return; // void
|
||||
}));
|
||||
}
|
||||
@ -23,12 +23,12 @@
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-expected.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/expected.hpp>
|
||||
#include <test-continuable.hpp>
|
||||
|
||||
using cti::detail::container::expected;
|
||||
using cti::exception_t;
|
||||
using cti::expected;
|
||||
|
||||
static int const CANARY = 373671;
|
||||
|
||||
@ -125,27 +125,6 @@ TYPED_TEST(expected_all_tests, is_error_move_assignable) {
|
||||
EXPECT_TRUE(e.is_exception());
|
||||
}
|
||||
|
||||
TYPED_TEST(expected_all_tests, is_value_assignable) {
|
||||
{
|
||||
TypeParam e;
|
||||
e = this->supply(CANARY);
|
||||
|
||||
EXPECT_TRUE(bool(e));
|
||||
EXPECT_EQ(this->get(*e), CANARY);
|
||||
EXPECT_TRUE(e.is_value());
|
||||
EXPECT_FALSE(e.is_exception());
|
||||
}
|
||||
|
||||
{
|
||||
TypeParam e;
|
||||
e = exception_t{};
|
||||
|
||||
EXPECT_FALSE(bool(e));
|
||||
EXPECT_FALSE(e.is_value());
|
||||
EXPECT_TRUE(e.is_exception());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(expected_copyable_tests, is_copy_constructible) {
|
||||
{
|
||||
copyable_type const e_old(CANARY);
|
||||
@ -190,6 +169,64 @@ TEST(expected_copyable_tests, is_copy_assignable) {
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(expected_all_tests, is_constructible_from_error_helper) {
|
||||
cti::exceptional_expected e1(exception_t{});
|
||||
{
|
||||
auto e2 = e1;
|
||||
}
|
||||
auto e2 = std::move(e1);
|
||||
|
||||
TypeParam e(std::move(e2));
|
||||
|
||||
EXPECT_FALSE(bool(e));
|
||||
EXPECT_FALSE(e.is_value());
|
||||
EXPECT_TRUE(e.is_exception());
|
||||
}
|
||||
|
||||
TYPED_TEST(expected_all_tests, is_assignable_from_error_helper) {
|
||||
cti::exceptional_expected e1(exception_t{});
|
||||
{
|
||||
auto e2 = e1;
|
||||
}
|
||||
auto e2 = std::move(e1);
|
||||
|
||||
TypeParam e;
|
||||
e = std::move(e2);
|
||||
|
||||
EXPECT_FALSE(bool(e));
|
||||
EXPECT_FALSE(e.is_value());
|
||||
EXPECT_TRUE(e.is_exception());
|
||||
}
|
||||
|
||||
TYPED_TEST(expected_all_tests, is_constructible_from_empty_helper) {
|
||||
cti::empty_expected e1;
|
||||
{
|
||||
auto e2 = e1;
|
||||
}
|
||||
auto e2 = std::move(e1);
|
||||
|
||||
TypeParam e(std::move(e2));
|
||||
|
||||
EXPECT_FALSE(bool(e));
|
||||
EXPECT_FALSE(e.is_value());
|
||||
EXPECT_TRUE(e.is_empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(expected_all_tests, is_assignable_from_empty_helper) {
|
||||
cti::empty_expected e1;
|
||||
{
|
||||
auto e2 = e1;
|
||||
}
|
||||
auto e2 = std::move(e1);
|
||||
|
||||
TypeParam e;
|
||||
e = std::move(e2);
|
||||
|
||||
EXPECT_FALSE(bool(e));
|
||||
EXPECT_FALSE(e.is_value());
|
||||
EXPECT_TRUE(e.is_empty());
|
||||
}
|
||||
|
||||
// This regression test shows a memory leak which happens when using the
|
||||
// expected class move constructed from another expected object.
|
||||
TEST(expected_single_test, test_leak_regression) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user