From 0525024e0519b51ddcc6fd322522d408f8b24352 Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Thu, 15 Mar 2018 09:27:42 +0100 Subject: [PATCH] Add support for co_await used with structured bindings --- include/continuable/detail/expected.hpp | 117 +++++++++++++++++- .../single/test-continuable-expected.cpp | 45 ++++++- 2 files changed, 157 insertions(+), 5 deletions(-) diff --git a/include/continuable/detail/expected.hpp b/include/continuable/detail/expected.hpp index 5ebe083..031943c 100644 --- a/include/continuable/detail/expected.hpp +++ b/include/continuable/detail/expected.hpp @@ -41,6 +41,12 @@ namespace cti { namespace detail { namespace container { +namespace detail { +/// A specialization which returns one of multiple error codes +template +struct expected_access; +} // namespace detail + /// A class similar to the one in the expected proposal, /// however it is capable of carrying an exception_ptr if /// exceptions are used. @@ -108,11 +114,25 @@ public: T const& operator*() const noexcept { return get_value(); } + + template + auto get() const& { + return detail::expected_access::access(*this); + } + template + auto get() && { + return detail::expected_access::access(std::move(*this)); + } }; namespace detail { struct void_guard_tag {}; +template +struct multiple_guard_tag { + std::tuple args; +}; + template struct expected_result_trait; template <> @@ -141,15 +161,18 @@ struct expected_result_trait> { }; template struct expected_result_trait> { - using expected_type = expected>; + using expected_type = + expected>>; static auto wrap(First first, Second second, Rest... rest) { - return std::make_tuple(std::move(first), std::move(second), - std::move(rest)...); + using tag = multiple_guard_tag, std::decay_t, + std::decay_t...>; + return tag{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()); + return std::move(e.get_value().args_); } }; } // namespace detail @@ -157,8 +180,94 @@ struct expected_result_trait> { template using expected_result_trait_t = detail::expected_result_trait{}))>; + +namespace detail { +/// The specialization which returns an error code instead of a value +template +struct expected_access<0U, Expected> { + template + static types::error_type access(expected const& e) { + if (e.is_exception()) { + return e.get_exception(); + } else { + return T{}; + } + } + template + static types::error_type access(expected&& e) { + if (e.is_exception()) { + return std::move(e.get_exception()); + } else { + return T{}; + } + } +}; +template +struct expected_access<1U, expected> { + static T access(expected const& e) { + if (e.is_value()) { + return e.get_value(); + } else { + return T{}; + } + } + static T access(expected&& e) { + if (e.is_value()) { + return std::move(e.get_value()); + } else { + return T{}; + } + } +}; +template +struct expected_access>> { + static auto access(expected> const& e) { + if (e.is_value()) { + return std::get(e.get_value()._args); + } else { + // Default construct the value + return std::tuple_element_t>{}; + } + } + static auto access(expected>&& e) { + if (e.is_value()) { + return std::get(std::move(e.get_value()._args)); + } else { + // Default construct the value + return std::tuple_element_t>{}; + } + } +}; +} // namespace detail + +/// Get specializtion mainly for using for co_await together +/// with structured bindings. +/*template +constexpr auto get(expected const& e) /// ... + -> decltype(detail::expected_access>(e)) { + return detail::expected_access>(e); +} +/// Get specializtion mainly for using for co_await together +/// with structured bindings. +template +constexpr auto get(expected&& e) /// ... + -> decltype(detail::expected_access>(std::move(e))) { + return detail::expected_access>(std::move(e)); +}*/ } // namespace container } // namespace detail } // namespace cti +/// \cond false +namespace std { +template +struct tuple_size> + : integral_constant {}; +template +struct tuple_size>> + : integral_constant {}; +} // namespace std +/// \endcond + #endif // CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED diff --git a/test/unit-test/single/test-continuable-expected.cpp b/test/unit-test/single/test-continuable-expected.cpp index d01297c..0f745ed 100644 --- a/test/unit-test/single/test-continuable-expected.cpp +++ b/test/unit-test/single/test-continuable-expected.cpp @@ -25,12 +25,14 @@ #include #include +#include #include #include -using cti::detail::types::error_type; using cti::detail::container::expected; +using cti::detail::traits::unpack; +using cti::detail::types::error_type; static int const CANARY = 373671; @@ -148,6 +150,47 @@ TYPED_TEST(expected_all_tests, is_value_assignable) { } } +/// This test tests whether an expected objects fulfills the requirements +/// to be used in C++17 structured bindings: +/// - std::tuple_size is specialized +/// - objects are retrieveable through std::get or through ADL get +TYPED_TEST(expected_all_tests, is_position_accessible) { + static_assert(std::tuple_size::value == 2, "Invalid size!"); + + { + TypeParam e(this->supply(CANARY)); + EXPECT_EQ(this->get(std::move(e).template get<1>()), CANARY); + } + + { + TypeParam e(this->supply(CANARY)); + + /*unpack( + [this](auto error, auto value) { + // ... + EXPECT_EQ(this->get(value), CANARY); + }, + std::move(e));*/ + } + + { + TypeParam e(error_type{}); + error_type other = std::move(e).template get<0>(); + (void)other; + } + + { + TypeParam e(error_type{}); + + /*unpack( + [this](auto error, auto value) { + // The value was default constructed + EXPECT_EQ(this->get(value), 0); + }, + std::move(e));*/ + } +} + TEST(expected_copyable_tests, is_copy_constructible) { { copyable_type const e_old(CANARY);