Add support for co_await used with structured bindings

This commit is contained in:
Denis Blank 2018-03-15 09:27:42 +01:00
parent b68cd1b43a
commit 0525024e05
2 changed files with 157 additions and 5 deletions

View File

@ -41,6 +41,12 @@
namespace cti { namespace cti {
namespace detail { namespace detail {
namespace container { namespace container {
namespace detail {
/// A specialization which returns one of multiple error codes
template <std::size_t I, typename Expected>
struct expected_access;
} // namespace detail
/// A class similar to the one in the expected proposal, /// A class similar to the one in the expected proposal,
/// however it is capable of carrying an exception_ptr if /// however it is capable of carrying an exception_ptr if
/// exceptions are used. /// exceptions are used.
@ -108,11 +114,25 @@ public:
T const& operator*() const noexcept { T const& operator*() const noexcept {
return get_value(); return get_value();
} }
template <std::size_t I>
auto get() const& {
return detail::expected_access<I, expected>::access(*this);
}
template <std::size_t I>
auto get() && {
return detail::expected_access<I, expected>::access(std::move(*this));
}
}; };
namespace detail { namespace detail {
struct void_guard_tag {}; struct void_guard_tag {};
template <typename... Args>
struct multiple_guard_tag {
std::tuple<Args...> args;
};
template <typename T> template <typename T>
struct expected_result_trait; struct expected_result_trait;
template <> template <>
@ -141,15 +161,18 @@ struct expected_result_trait<traits::identity<T>> {
}; };
template <typename First, typename Second, typename... Rest> template <typename First, typename Second, typename... Rest>
struct expected_result_trait<traits::identity<First, Second, Rest...>> { struct expected_result_trait<traits::identity<First, Second, Rest...>> {
using expected_type = expected<std::tuple<First, Second, Rest...>>; using expected_type =
expected<multiple_guard_tag<std::tuple<First, Second, Rest...>>>;
static auto wrap(First first, Second second, Rest... rest) { static auto wrap(First first, Second second, Rest... rest) {
return std::make_tuple(std::move(first), std::move(second), using tag = multiple_guard_tag<std::decay_t<First>, std::decay_t<Second>,
std::move(rest)...); std::decay_t<Rest>...>;
return tag{std::make_tuple(std::move(first), std::move(second),
std::move(rest)...)};
} }
static auto unwrap(expected_type&& e) { static auto unwrap(expected_type&& e) {
assert(e.is_value()); assert(e.is_value());
return std::move(e.get_value()); return std::move(e.get_value().args_);
} }
}; };
} // namespace detail } // namespace detail
@ -157,8 +180,94 @@ struct expected_result_trait<traits::identity<First, Second, Rest...>> {
template <typename Continuable> template <typename Continuable>
using expected_result_trait_t = detail::expected_result_trait<decltype( using expected_result_trait_t = detail::expected_result_trait<decltype(
hints::hint_of(traits::identify<Continuable>{}))>; hints::hint_of(traits::identify<Continuable>{}))>;
namespace detail {
/// The specialization which returns an error code instead of a value
template <typename Expected>
struct expected_access<0U, Expected> {
template <typename T>
static types::error_type access(expected<T> const& e) {
if (e.is_exception()) {
return e.get_exception();
} else {
return T{};
}
}
template <typename T>
static types::error_type access(expected<T>&& e) {
if (e.is_exception()) {
return std::move(e.get_exception());
} else {
return T{};
}
}
};
template <typename T>
struct expected_access<1U, expected<T>> {
static T access(expected<T> const& e) {
if (e.is_value()) {
return e.get_value();
} else {
return T{};
}
}
static T access(expected<T>&& e) {
if (e.is_value()) {
return std::move(e.get_value());
} else {
return T{};
}
}
};
template <std::size_t I, typename... Args>
struct expected_access<I, expected<multiple_guard_tag<Args...>>> {
static auto access(expected<multiple_guard_tag<Args...>> const& e) {
if (e.is_value()) {
return std::get<I - 1>(e.get_value()._args);
} else {
// Default construct the value
return std::tuple_element_t<I - 1, std::tuple<Args...>>{};
}
}
static auto access(expected<multiple_guard_tag<Args...>>&& e) {
if (e.is_value()) {
return std::get<I - 1>(std::move(e.get_value()._args));
} else {
// Default construct the value
return std::tuple_element_t<I - 1, std::tuple<Args...>>{};
}
}
};
} // namespace detail
/// Get specializtion mainly for using for co_await together
/// with structured bindings.
/*template <std::size_t I, typename T>
constexpr auto get(expected<T> const& e) /// ...
-> decltype(detail::expected_access<I, expected<T>>(e)) {
return detail::expected_access<I, expected<T>>(e);
}
/// Get specializtion mainly for using for co_await together
/// with structured bindings.
template <std::size_t I, typename T>
constexpr auto get(expected<T>&& e) /// ...
-> decltype(detail::expected_access<I, expected<T>>(std::move(e))) {
return detail::expected_access<I, expected<T>>(std::move(e));
}*/
} // namespace container } // namespace container
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti
/// \cond false
namespace std {
template <typename T>
struct tuple_size<cti::detail::container::expected<T>>
: integral_constant<std::size_t, 2> {};
template <typename... Args>
struct tuple_size<cti::detail::container::expected<
cti::detail::container::detail::multiple_guard_tag<Args...>>>
: integral_constant<std::size_t, 1 + sizeof...(Args)> {};
} // namespace std
/// \endcond
#endif // CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED

View File

@ -25,12 +25,14 @@
#include <utility> #include <utility>
#include <continuable/detail/expected.hpp> #include <continuable/detail/expected.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/types.hpp> #include <continuable/detail/types.hpp>
#include <test-continuable.hpp> #include <test-continuable.hpp>
using cti::detail::types::error_type;
using cti::detail::container::expected; using cti::detail::container::expected;
using cti::detail::traits::unpack;
using cti::detail::types::error_type;
static int const CANARY = 373671; 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<TypeParam>::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) { TEST(expected_copyable_tests, is_copy_constructible) {
{ {
copyable_type const e_old(CANARY); copyable_type const e_old(CANARY);