forked from TartanLlama/expected
Merge branch 'master' of github.com:TartanLlama/expected
This commit is contained in:
@@ -10,6 +10,7 @@ target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
|
||||
# Make test executable
|
||||
set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests/extensions.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests/assignment.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tests/constructors.cpp)
|
||||
|
||||
add_executable(tests ${TEST_SOURCES})
|
||||
|
121
COPYING
Normal file
121
COPYING
Normal file
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
373
expected.hpp
373
expected.hpp
@@ -18,6 +18,7 @@
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
|
||||
#if (defined(_MSC_VER) && _MSC_VER == 1900)
|
||||
#define TL_EXPECTED_MSVC2015
|
||||
@@ -41,7 +42,7 @@
|
||||
|
||||
#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
|
||||
defined(TL_EXPECTED_GCC49)) && \
|
||||
!defined(TL_EXPECTED_GCC50)
|
||||
!defined(TL_EXPECTED_GCC54)
|
||||
/// \exclude
|
||||
#define TL_EXPECTED_11_CONSTEXPR
|
||||
#else
|
||||
@@ -57,6 +58,13 @@ template <bool E, class T = void>
|
||||
using enable_if_t = typename std::enable_if<E, T>::type;
|
||||
template <class T> using decay_t = typename std::decay<T>::type;
|
||||
|
||||
// std::conjunction from C++17
|
||||
template <class...> struct conjunction : std::true_type {};
|
||||
template <class B> struct conjunction<B> : B {};
|
||||
template <class B, class... Bs>
|
||||
struct conjunction<B, Bs...>
|
||||
: std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
|
||||
|
||||
// Trait for checking if a type is a tl::expected
|
||||
template <class T> struct is_expected_impl : std::false_type {};
|
||||
template <class T, class E>
|
||||
@@ -155,6 +163,11 @@ constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
||||
return lhs.value() >= rhs.value();
|
||||
}
|
||||
|
||||
template <class E>
|
||||
unexpected<E> make_unexpected (E&& e) {
|
||||
return unexpected<E>(std::forward<E>(e));
|
||||
}
|
||||
|
||||
struct unexpect_t {
|
||||
unexpect_t() = default;
|
||||
};
|
||||
@@ -385,7 +398,51 @@ template <class E> struct expected_storage_base<void, E, false, false> {
|
||||
};
|
||||
|
||||
// TODO, conditionally delete things
|
||||
template <class T, class E> class expected_ctor_base {};
|
||||
template <class T, class E> struct expected_ctor_base {};
|
||||
template <class T, class E,
|
||||
bool = (std::is_copy_assignable<T>::value && std::is_copy_assignable<E>::value &&
|
||||
std::is_copy_constructible<E>::value && std::is_copy_constructible<T>::value &&
|
||||
std::is_nothrow_move_constructible<E>::value),
|
||||
bool = (std::is_move_constructible<T>::value && std::is_move_assignable<T>::value &&
|
||||
std::is_nothrow_move_constructible<E>::value && std::is_nothrow_move_assignable<E>::value)>
|
||||
struct expected_assign_base {
|
||||
expected_assign_base() = default;
|
||||
~expected_assign_base() = default;
|
||||
expected_assign_base(const expected_assign_base&) = default;
|
||||
expected_assign_base(expected_assign_base&&) noexcept = default;
|
||||
expected_assign_base& operator=(const expected_assign_base&) = default;
|
||||
expected_assign_base& operator=(expected_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
struct expected_assign_base<T,E,true,false> {
|
||||
expected_assign_base() = default;
|
||||
~expected_assign_base() = default;
|
||||
expected_assign_base(const expected_assign_base&) = default;
|
||||
expected_assign_base(expected_assign_base&&) noexcept = default;
|
||||
expected_assign_base& operator=(const expected_assign_base&) = default;
|
||||
expected_assign_base& operator=(expected_assign_base&&) noexcept = delete;
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
struct expected_assign_base<T,E,false,true> {
|
||||
expected_assign_base() = default;
|
||||
~expected_assign_base() = default;
|
||||
expected_assign_base(const expected_assign_base&) = default;
|
||||
expected_assign_base(expected_assign_base&&) noexcept = default;
|
||||
expected_assign_base& operator=(const expected_assign_base&) = delete;
|
||||
expected_assign_base& operator=(expected_assign_base&&) noexcept = default;
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
struct expected_assign_base<T,E,false,false> {
|
||||
expected_assign_base() = default;
|
||||
~expected_assign_base() = default;
|
||||
expected_assign_base(const expected_assign_base&) = default;
|
||||
expected_assign_base(expected_assign_base&&) noexcept = default;
|
||||
expected_assign_base& operator=(const expected_assign_base&) = delete;
|
||||
expected_assign_base& operator=(expected_assign_base&&) noexcept = delete;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class E> class bad_expected_access : public std::exception {
|
||||
@@ -407,7 +464,8 @@ private:
|
||||
|
||||
template <class T, class E>
|
||||
class expected : private detail::expected_storage_base<T, E>,
|
||||
private detail::expected_ctor_base<T, E> {
|
||||
private detail::expected_assign_base<T, E>
|
||||
{
|
||||
static_assert(!std::is_reference<T>::value, "T must not be a reference");
|
||||
static_assert(!std::is_same<T, std::remove_cv<in_place_t>>::value,
|
||||
"T must not be in_place_t");
|
||||
@@ -432,7 +490,7 @@ public:
|
||||
typedef E error_type;
|
||||
typedef unexpected<E> unexpected_type;
|
||||
|
||||
#ifdef TL_EXPECTED_CXX14
|
||||
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54)
|
||||
/// \group and_then
|
||||
/// Carries out some operation which returns an optional on the stored object
|
||||
/// if there is one. \requires `std::invoke(std::forward<F>(f), value())`
|
||||
@@ -546,7 +604,7 @@ public:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TL_EXPECTED_CXX14
|
||||
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54)
|
||||
/// \brief Carries out some operation on the stored object if there is one.
|
||||
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
|
||||
/// value())`. Returns a `std::expected<U>`. The return value is empty if
|
||||
@@ -606,7 +664,7 @@ public:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TL_EXPECTED_CXX14
|
||||
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54)
|
||||
/// \brief Carries out some operation on the stored object if there is one.
|
||||
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
|
||||
/// value())`. Returns a `std::expected<U>`. The return value is empty if
|
||||
@@ -688,14 +746,14 @@ public:
|
||||
detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
|
||||
nullptr>
|
||||
explicit constexpr expected(unexpected<G> const &e)
|
||||
: storage_base(unexpect, e) {}
|
||||
: storage_base(unexpect, e.value()) {}
|
||||
|
||||
template <
|
||||
class G = E,
|
||||
detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
|
||||
nullptr,
|
||||
detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
|
||||
constexpr expected(unexpected<G> const &e) : storage_base(unexpect, e) {}
|
||||
constexpr expected(unexpected<G> const &e) : storage_base(unexpect, e.value()) {}
|
||||
|
||||
template <
|
||||
class G = E,
|
||||
@@ -703,7 +761,7 @@ public:
|
||||
detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
|
||||
explicit constexpr expected(unexpected<G> &&e) noexcept(
|
||||
std::is_nothrow_constructible<E, G &&>::value)
|
||||
: storage_base(unexpect, std::move(e)) {}
|
||||
: storage_base(unexpect, std::move(e.value())) {}
|
||||
|
||||
template <
|
||||
class G = E,
|
||||
@@ -711,7 +769,7 @@ public:
|
||||
detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
|
||||
constexpr expected(unexpected<G> &&e) noexcept(
|
||||
std::is_nothrow_constructible<E, G &&>::value)
|
||||
: storage_base(unexpect, std::move(e)) {}
|
||||
: storage_base(unexpect, std::move(e.value())) {}
|
||||
|
||||
template <class... Args,
|
||||
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
|
||||
@@ -794,15 +852,164 @@ public:
|
||||
std::is_convertible<U &&, T>::value> * = nullptr>
|
||||
constexpr expected(U &&v) : expected(in_place, std::forward<U>(v)) {}
|
||||
|
||||
// TODO
|
||||
expected &operator=(const expected &);
|
||||
expected &operator=(expected &&) noexcept;
|
||||
template <class U = T> expected &operator=(U &&);
|
||||
template <class G = E> expected &operator=(const unexpected<G> &);
|
||||
template <class G = E> expected &operator=(unexpected<G> &&) noexcept;
|
||||
template <class... Args> void emplace(Args &&...);
|
||||
template <class U, class... Args>
|
||||
void emplace(std::initializer_list<U>, Args &&...);
|
||||
// TODO conditionally delete
|
||||
expected& operator=(const expected& rhs) {
|
||||
return assign(rhs);
|
||||
}
|
||||
|
||||
expected& operator=(expected&& rhs) {
|
||||
return assign(std::move(rhs));
|
||||
}
|
||||
|
||||
template <class U = T,
|
||||
detail::enable_if_t<
|
||||
(!std::is_same<expected<T,E>, detail::decay_t<U>>::value &&
|
||||
!detail::conjunction<std::is_scalar<T>, std::is_same<T, detail::decay_t<U>>>::value &&
|
||||
std::is_constructible<T, U>::value &&
|
||||
std::is_assignable<T&, U>::value &&
|
||||
std::is_nothrow_move_constructible<E>::value)>* = nullptr,
|
||||
detail::enable_if_t<std::is_nothrow_constructible<T, U&&>::value>* = nullptr>
|
||||
expected &operator=(U &&v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
}
|
||||
else {
|
||||
err().~unexpected<E>();
|
||||
::new (valptr()) T (std::forward<U>(v));
|
||||
this->m_has_val = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class U = T,
|
||||
detail::enable_if_t<
|
||||
(!std::is_same<expected<T,E>, detail::decay_t<U>>::value &&
|
||||
!detail::conjunction<std::is_scalar<T>, std::is_same<T, detail::decay_t<U>>>::value &&
|
||||
std::is_constructible<T, U>::value &&
|
||||
std::is_assignable<T&, U>::value &&
|
||||
std::is_nothrow_move_constructible<E>::value)>* = nullptr,
|
||||
detail::enable_if_t<!std::is_nothrow_constructible<T, U&&>::value>* = nullptr>
|
||||
expected &operator=(U &&v) {
|
||||
if (has_value()) {
|
||||
val() = std::forward<U>(v);
|
||||
}
|
||||
else {
|
||||
auto tmp = std::move(err());
|
||||
err().~unexpected<E>();
|
||||
try {
|
||||
::new (valptr()) T (std::move(v));
|
||||
this->m_has_val = true;
|
||||
}
|
||||
catch (...) {
|
||||
err() = std::move(tmp);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <class G = E,
|
||||
detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value && std::is_assignable<G&, G>::value>* = nullptr>
|
||||
expected &operator=(const unexpected<G> &rhs) {
|
||||
if (!has_value()) {
|
||||
err() = rhs;
|
||||
}
|
||||
else {
|
||||
val().~T();
|
||||
::new (errptr()) unexpected<E> (rhs);
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class G = E,
|
||||
detail::enable_if_t<std::is_nothrow_move_constructible<G>::value && std::is_move_assignable<G>::value>* = nullptr>
|
||||
expected &operator=(unexpected<G> && rhs) noexcept {
|
||||
if (!has_value()) {
|
||||
err() = std::move(rhs);
|
||||
}
|
||||
else {
|
||||
val().~T();
|
||||
::new (errptr()) unexpected<E> (std::move(rhs));
|
||||
this->m_has_val = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class... Args,
|
||||
detail::enable_if_t<std::is_nothrow_constructible<T, Args&&...>::value>* = nullptr>
|
||||
void emplace(Args &&... args) {
|
||||
if (has_value()) {
|
||||
val() = T(std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
err().~unexpected<E>();
|
||||
::new (valptr()) T (std::forward<Args>(args)...);
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args,
|
||||
detail::enable_if_t<!std::is_nothrow_constructible<T, Args&&...>::value>* = nullptr>
|
||||
void emplace(Args &&... args) {
|
||||
if (has_value()) {
|
||||
val() = T(std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
auto tmp = std::move(err());
|
||||
err().~unexpected<E>();
|
||||
|
||||
try {
|
||||
::new (valptr()) T (std::forward<Args>(args)...);
|
||||
this->m_has_val = true;
|
||||
}
|
||||
catch (...) {
|
||||
err() = std::move(tmp);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class U, class... Args,
|
||||
detail::enable_if_t<std::is_nothrow_constructible<T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args &&... args) {
|
||||
if (has_value()) {
|
||||
T t (il, std::forward<Args>(args)...);
|
||||
val() = std::move(t);
|
||||
}
|
||||
else {
|
||||
err().~unexpected<E>();
|
||||
::new (valptr()) T (il, std::forward<Args>(args)...);
|
||||
this->m_has_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class U, class... Args,
|
||||
detail::enable_if_t<!std::is_nothrow_constructible<T, std::initializer_list<U>&, Args&&...>::value>* = nullptr>
|
||||
void emplace(std::initializer_list<U> il, Args &&... args) {
|
||||
if (has_value()) {
|
||||
T t (il, std::forward<Args>(args)...);
|
||||
val() = std::move(t);
|
||||
}
|
||||
else {
|
||||
auto tmp = std::move(err());
|
||||
err().~unexpected<E>();
|
||||
|
||||
try {
|
||||
::new (valptr()) T (il, std::forward<Args>(args)...);
|
||||
this->m_has_val = true;
|
||||
}
|
||||
catch (...) {
|
||||
err() = std::move(tmp);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO SFINAE
|
||||
void swap(expected &rhs) noexcept(
|
||||
@@ -874,7 +1081,109 @@ public:
|
||||
return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template <class U = T, detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>* = nullptr>
|
||||
expected &assign(const expected &rhs) noexcept {
|
||||
if (!has_value() && rhs.has_value()) {
|
||||
err().~unexpected<E>();
|
||||
::new (valptr()) T (*rhs);
|
||||
this->m_has_val = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
return assign_common(rhs);
|
||||
}
|
||||
|
||||
template <class U = T, detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value && std::is_nothrow_move_constructible<U>::value>* = nullptr>
|
||||
expected &assign(const expected &rhs) noexcept {
|
||||
if (!has_value() && rhs.has_value()) {
|
||||
T tmp = *rhs;
|
||||
err().~unexpected<E>();
|
||||
::new (valptr()) T (std::move(tmp));
|
||||
this->m_has_val = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
return assign_common(rhs);
|
||||
}
|
||||
|
||||
template <class U = T, detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value && !std::is_nothrow_move_constructible<U>::value>* = nullptr>
|
||||
expected &assign(const expected &rhs) {
|
||||
if (!has_value() && rhs.has_value()) {
|
||||
auto tmp = std::move(err());
|
||||
err().~unexpected<E>();
|
||||
|
||||
try {
|
||||
::new (valptr()) T (*rhs);
|
||||
this->m_has_val = true;
|
||||
}
|
||||
catch(...) {
|
||||
err() = std::move(tmp);
|
||||
throw;
|
||||
}
|
||||
this->m_has_val = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
return assign_common(rhs);
|
||||
}
|
||||
|
||||
template <class U = T, detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>* = nullptr>
|
||||
expected &assign(expected && rhs) noexcept {
|
||||
if (!has_value() && rhs.has_value()) {
|
||||
err().~unexpected<E>();
|
||||
::new (valptr()) T (*std::move(rhs));
|
||||
this->m_has_val = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
return assign_common(rhs);
|
||||
}
|
||||
|
||||
template <class U = T, detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>* = nullptr>
|
||||
expected &assign(expected && rhs) {
|
||||
if (!has_value() && rhs.has_value()) {
|
||||
auto tmp = std::move(err());
|
||||
err().~unexpected<E>();
|
||||
try {
|
||||
::new (valptr()) T (*std::move(rhs));
|
||||
this->m_has_val = true;
|
||||
}
|
||||
catch (...) {
|
||||
err() = std::move(tmp);
|
||||
throw;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
return assign_common(rhs);
|
||||
}
|
||||
|
||||
|
||||
template <class Rhs>
|
||||
expected& assign_common(Rhs&& rhs) {
|
||||
if (has_value()) {
|
||||
if (rhs.has_value()) {
|
||||
val() = *std::forward<Rhs>(rhs);
|
||||
}
|
||||
else {
|
||||
val().~T();
|
||||
::new (errptr()) unexpected<E> (std::forward<Rhs>(rhs).err());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!rhs.has_value()) {
|
||||
err() = std::forward<Rhs>(rhs).err();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
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>>;
|
||||
|
||||
@@ -883,7 +1192,7 @@ private:
|
||||
class Ret = decltype(detail::invoke(std::declval<F>(),
|
||||
*std::declval<Exp>())),
|
||||
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
|
||||
static constexpr auto map_impl(Exp &&exp, F &&f) {
|
||||
constexpr auto map_impl(Exp &&exp, F &&f) {
|
||||
using result = ret_t<Exp, Ret>;
|
||||
return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
|
||||
*std::forward<Exp>(exp)))
|
||||
@@ -894,7 +1203,7 @@ private:
|
||||
class Ret = decltype(detail::invoke(std::declval<F>(),
|
||||
*std::declval<Exp>())),
|
||||
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
|
||||
static auto map_impl(Exp &&exp, F &&f) {
|
||||
auto map_impl(Exp &&exp, F &&f) {
|
||||
using result = expected<monostate, err_t<Exp>>;
|
||||
if (exp.has_value()) {
|
||||
detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
|
||||
@@ -909,7 +1218,7 @@ private:
|
||||
*std::declval<Exp>())),
|
||||
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
|
||||
|
||||
static constexpr auto map_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> {
|
||||
constexpr auto map_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> {
|
||||
using result = ret_t<Exp, Ret>;
|
||||
|
||||
return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
|
||||
@@ -922,7 +1231,7 @@ private:
|
||||
*std::declval<Exp>())),
|
||||
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
|
||||
|
||||
static auto map_impl(Exp &&exp, F &&f) -> expected<monostate, err_t<Exp>> {
|
||||
auto map_impl(Exp &&exp, F &&f) -> expected<monostate, err_t<Exp>> {
|
||||
if (exp.has_value()) {
|
||||
detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
|
||||
return tl::monostate{};
|
||||
@@ -932,11 +1241,11 @@ private:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TL_EXPECTED_CXX14
|
||||
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && !defined(TL_EXPECTED_GCC54)
|
||||
template <class Exp, class F,
|
||||
class Ret = decltype(detail::invoke(std::declval<F>(),
|
||||
*std::declval<Exp>()))>
|
||||
static constexpr auto map_error_impl(Exp &&exp, F &&f) {
|
||||
constexpr auto map_error_impl(Exp &&exp, F &&f) {
|
||||
using result = ret_t<Exp, Ret>;
|
||||
return exp.has_value()
|
||||
? result(*std::forward<Exp>(exp))
|
||||
@@ -948,15 +1257,19 @@ private:
|
||||
template <class Exp, class F,
|
||||
class Ret = decltype(detail::invoke(std::declval<F>(),
|
||||
*std::declval<Exp>()))>
|
||||
static constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> {
|
||||
constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> {
|
||||
using result = ret_t<Exp, Ret>;
|
||||
|
||||
return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
|
||||
*std::forward<Exp>(exp)))
|
||||
: result(unexpect, std::forward<Exp>(exp).error());
|
||||
return exp.has_value()
|
||||
? result(*std::forward<Exp>(exp))
|
||||
: result(unexpect,
|
||||
detail::invoke(std::forward<F>(f),
|
||||
std::forward<Exp>(exp).error()));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
// TODO
|
||||
template <class E> class expected<void, E> {};
|
||||
|
76
tests/assignment.cpp
Normal file
76
tests/assignment.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "catch.hpp"
|
||||
#include "expected.hpp"
|
||||
|
||||
TEST_CASE("Simple assignment", "[assignment.simple]") {
|
||||
tl::expected<int,int> e1 = 42;
|
||||
tl::expected<int,int> e2 = 17;
|
||||
tl::expected<int,int> e3 = 21;
|
||||
tl::expected<int,int> e4 = tl::make_unexpected(42);
|
||||
tl::expected<int,int> e5 = tl::make_unexpected(17);
|
||||
tl::expected<int,int> e6 = tl::make_unexpected(21);
|
||||
|
||||
e1 = e2;
|
||||
REQUIRE(e1);
|
||||
REQUIRE(*e1 == 17);
|
||||
REQUIRE(e2);
|
||||
REQUIRE(*e2 == 17);
|
||||
|
||||
e1 = std::move(e2);
|
||||
REQUIRE(e1);
|
||||
REQUIRE(*e1 == 17);
|
||||
REQUIRE(e2);
|
||||
REQUIRE(*e2 == 17);
|
||||
|
||||
e1 = 42;
|
||||
REQUIRE(e1);
|
||||
REQUIRE(*e1 == 42);
|
||||
|
||||
auto unex = tl::make_unexpected(12);
|
||||
e1 = unex;
|
||||
REQUIRE(!e1);
|
||||
REQUIRE(e1.error() == 12);
|
||||
|
||||
e1 = tl::make_unexpected(42);
|
||||
REQUIRE(!e1);
|
||||
REQUIRE(e1.error() == 42);
|
||||
|
||||
e1 = e3;
|
||||
REQUIRE(e1);
|
||||
REQUIRE(*e1 == 21);
|
||||
|
||||
e4 = e5;
|
||||
REQUIRE(!e4);
|
||||
REQUIRE(e4.error() == 17);
|
||||
|
||||
e4 = std::move(e6);
|
||||
REQUIRE(!e4);
|
||||
REQUIRE(e4.error() == 21);
|
||||
|
||||
e4 = e1;
|
||||
REQUIRE(e4);
|
||||
REQUIRE(*e4 == 21);
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Assignment deletion", "[assignment.deletion]") {
|
||||
struct has_all {
|
||||
has_all() = default;
|
||||
has_all(const has_all&) = default;
|
||||
has_all(has_all&&) noexcept = default;
|
||||
has_all& operator=(const has_all&) = default;
|
||||
};
|
||||
|
||||
tl::expected<has_all, has_all> e1 = {};
|
||||
tl::expected<has_all, has_all> e2 = {};
|
||||
e1 = e2;
|
||||
|
||||
struct except_move {
|
||||
except_move() = default;
|
||||
except_move(const except_move&) = default;
|
||||
except_move(except_move&&) noexcept(false) {};
|
||||
except_move& operator=(const except_move&) = default;
|
||||
};
|
||||
tl::expected<except_move, except_move> e3 = {};
|
||||
tl::expected<except_move, except_move> e4 = {};
|
||||
e3 = e4;
|
||||
}
|
4
tests/emplace.cpp
Normal file
4
tests/emplace.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "catch.hpp"
|
||||
#include "expected.hpp"
|
||||
|
||||
TEST_CASE("Emplace", "[emplace]") {
|
Reference in New Issue
Block a user