More void support

This commit is contained in:
Simon Brand
2017-12-23 15:13:38 +00:00
parent 468c403c91
commit 2d7dfc8b02
3 changed files with 118 additions and 76 deletions

View File

@@ -15,6 +15,13 @@ TEST_CASE("Triviality", "[bases.triviality]") {
REQUIRE(std::is_trivially_move_assignable<tl::expected<int,int>>::value); REQUIRE(std::is_trivially_move_assignable<tl::expected<int,int>>::value);
REQUIRE(std::is_trivially_destructible<tl::expected<int,int>>::value); REQUIRE(std::is_trivially_destructible<tl::expected<int,int>>::value);
REQUIRE(std::is_trivially_copy_constructible<tl::expected<void,int>>::value);
REQUIRE(std::is_trivially_copy_assignable<tl::expected<void,int>>::value);
REQUIRE(std::is_trivially_move_constructible<tl::expected<void,int>>::value);
REQUIRE(std::is_trivially_move_assignable<tl::expected<void,int>>::value);
REQUIRE(std::is_trivially_destructible<tl::expected<void,int>>::value);
{ {
struct T { struct T {
T(const T&) = default; T(const T&) = default;

View File

@@ -120,4 +120,15 @@ TEST_CASE("Constructors", "[constructors]") {
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value); REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
# endif # endif
} }
{
tl::expected<void,int> e;
REQUIRE(e);
}
{
tl::expected<void,int> e (tl::unexpect, 42);
REQUIRE(!e);
REQUIRE(e.error() == 42);
}
} }

View File

@@ -19,7 +19,6 @@
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <iostream>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@@ -533,6 +532,12 @@ struct expected_operations_base : expected_storage_base<T, E> {
this->m_has_val = true; this->m_has_val = true;
} }
template <class Rhs>
void construct_with(Rhs && rhs) noexcept {
new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
this->m_has_val = true;
}
template <class... Args> void construct_error(Args &&... args) noexcept { template <class... Args> void construct_error(Args &&... args) noexcept {
new (std::addressof(this->m_unexpect)) new (std::addressof(this->m_unexpect))
unexpected<E>(std::forward<Args>(args)...); unexpected<E>(std::forward<Args>(args)...);
@@ -678,6 +683,12 @@ struct expected_operations_base<void,E> : expected_storage_base<void, E> {
this->m_has_val = true; this->m_has_val = true;
} }
template <class Rhs>
void construct_with(Rhs && rhs) noexcept {
this->m_has_val = true;
}
template <class... Args> void construct_error(Args &&... args) noexcept { template <class... Args> void construct_error(Args &&... args) noexcept {
new (std::addressof(this->m_unexpect)) new (std::addressof(this->m_unexpect))
unexpected<E>(std::forward<Args>(args)...); unexpected<E>(std::forward<Args>(args)...);
@@ -735,7 +746,7 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
expected_copy_base(const expected_copy_base &rhs) expected_copy_base(const expected_copy_base &rhs)
: expected_operations_base<T, E>(no_init) { : expected_operations_base<T, E>(no_init) {
if (rhs.has_value()) { if (rhs.has_value()) {
this->construct(rhs.get()); this->construct_with(rhs);
} else { } else {
this->construct_error(rhs.geterr()); this->construct_error(rhs.geterr());
} }
@@ -772,7 +783,7 @@ struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
std::is_nothrow_move_constructible<T>::value) std::is_nothrow_move_constructible<T>::value)
: expected_copy_base<T, E>(no_init) { : expected_copy_base<T, E>(no_init) {
if (rhs.has_value()) { if (rhs.has_value()) {
this->construct(std::move(rhs.get())); this->construct_with(std::move(rhs));
} else { } else {
this->construct_error(std::move(rhs.geterr())); this->construct_error(std::move(rhs.geterr()));
} }
@@ -1072,46 +1083,26 @@ public:
/// is returned. /// is returned.
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
using result = detail::invoke_result_t<F, T &>; return and_then_impl(*this, std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(unexpect, this->error());
} }
/// \group and_then /// \group and_then
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &&; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &&;
template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
using result = detail::invoke_result_t<F, T &&>; return and_then_impl(std::move(*this), std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: result(unexpect, std::move(this->error()));
} }
/// \group and_then /// \group and_then
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &;
template <class F> constexpr auto and_then(F &&f) const & { template <class F> constexpr auto and_then(F &&f) const & {
using result = detail::invoke_result_t<F, const T &>; return and_then_impl(*this, std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(unexpect, this->error());
} }
#ifndef TL_EXPECTED_NO_CONSTRR #ifndef TL_EXPECTED_NO_CONSTRR
/// \group and_then /// \group and_then
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &&; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &&;
template <class F> constexpr auto and_then(F &&f) const && { template <class F> constexpr auto and_then(F &&f) const && {
using result = detail::invoke_result_t<F, const T &&>; return and_then_impl(std::move(*this), std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: result(unexpect, std::move(this->error()));
} }
#endif #endif
@@ -1126,50 +1117,30 @@ public:
/// is returned. /// is returned.
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
template <class F> template <class F>
TL_EXPECTED_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) & { TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl(*this, std::forward<F>(f))) {
using result = detail::invoke_result_t<F, T &>; return and_then_impl(*this, std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(unexpect, this->error());
} }
/// \group and_then /// \group and_then
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &&; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &&;
template <class F> template <class F>
TL_EXPECTED_11_CONSTEXPR detail::invoke_result_t<F, T &&> and_then(F &&f) && { TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl(std::move(*this), std::forward<F>(f))) {
using result = detail::invoke_result_t<F, T &&>; return and_then_impl(std::move(*this), std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(unexpect, std::move(this->error()));
} }
/// \group and_then /// \group and_then
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &;
template <class F> template <class F>
constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const & { constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(*this, std::forward<F>(f))) {
using result = detail::invoke_result_t<F, const T &>; return and_then_impl(*this, std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(unexpect, this->error());
} }
#ifndef TL_EXPECTED_NO_CONSTRR #ifndef TL_EXPECTED_NO_CONSTRR
/// \group and_then /// \group and_then
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &&; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) const &&;
template <class F> template <class F>
constexpr detail::invoke_result_t<F, const T &&> and_then(F &&f) const && { constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(std::move(*this), std::forward<F>(f))) {
using result = detail::invoke_result_t<F, const T &&>; return and_then_impl(std::move(*this), std::forward<F>(f));
static_assert(detail::is_expected<result>::value,
"F must return an expected");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(unexpect, std::move(this->error()));
} }
#endif #endif
#endif #endif
@@ -1514,15 +1485,16 @@ public:
template < template <
class U = T, class U = T,
class G = T,
detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * = detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
nullptr, nullptr,
detail::enable_if_t<!std::is_void<U>::value>* = nullptr, detail::enable_if_t<!std::is_void<G>::value>* = nullptr,
detail::enable_if_t< detail::enable_if_t<
(!std::is_same<expected<T, E>, detail::decay_t<U>>::value && (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
!detail::conjunction<std::is_scalar<T>, !detail::conjunction<std::is_scalar<T>,
std::is_same<T, detail::decay_t<U>>>::value && std::is_same<T, detail::decay_t<U>>>::value &&
std::is_constructible<T, U>::value && std::is_constructible<T, U>::value &&
// std::is_assignable<T &, U>::value && std::is_assignable<G &, U>::value &&
std::is_nothrow_move_constructible<E>::value)> * = nullptr std::is_nothrow_move_constructible<E>::value)> * = nullptr
> >
expected &operator=(U &&v) { expected &operator=(U &&v) {
@@ -1540,6 +1512,7 @@ public:
/// \exclude /// \exclude
template < template <
class U = T, class U = T,
class G = T,
detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * = detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
nullptr, nullptr,
detail::enable_if_t<!std::is_void<U>::value>* = nullptr, detail::enable_if_t<!std::is_void<U>::value>* = nullptr,
@@ -1548,7 +1521,7 @@ public:
!detail::conjunction<std::is_scalar<T>, !detail::conjunction<std::is_scalar<T>,
std::is_same<T, detail::decay_t<U>>>::value && std::is_same<T, detail::decay_t<U>>>::value &&
std::is_constructible<T, U>::value && std::is_constructible<T, U>::value &&
// std::is_assignable<T &, U>::value && std::is_assignable<G &, U>::value &&
std::is_nothrow_move_constructible<E>::value)> * = nullptr std::is_nothrow_move_constructible<E>::value)> * = nullptr
> >
expected &operator=(U &&v) { expected &operator=(U &&v) {
@@ -1782,10 +1755,61 @@ public:
/// \exclude /// \exclude
namespace detail { namespace detail {
template <class Exp> using exp_t = typename detail::decay_t<Exp>::error_type; template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type;
template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type; template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>; template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
#ifdef TL_EXPECTED_CXX14
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
constexpr auto and_then_impl(Exp &&exp, F &&f) {
static_assert(detail::is_expected<Ret>::value,
"F must return an expected");
return exp.has_value() ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
: Ret(unexpect, exp.error());
}
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
constexpr auto and_then_impl(Exp &&exp, F &&f) {
static_assert(detail::is_expected<Ret>::value,
"F must return an expected");
return exp.has_value() ? detail::invoke(std::forward<F>(f))
: Ret(unexpect, exp.error());
}
#else
template<class>struct TC;
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
auto and_then_impl(Exp &&exp, F &&f) -> Ret {
static_assert(detail::is_expected<Ret>::value,
"F must return an expected");
return exp.has_value() ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
: Ret(unexpect, exp.error());
}
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
static_assert(detail::is_expected<Ret>::value,
"F must return an expected");
return exp.has_value() ? detail::invoke(std::forward<F>(f))
: Ret(unexpect, exp.error());
}
#endif
#ifdef TL_EXPECTED_CXX14 #ifdef TL_EXPECTED_CXX14
template <class Exp, class F, template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(), class Ret = decltype(detail::invoke(std::declval<F>(),