--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Like folly::Optional, but can store a value *or* and error.
+ *
+ * @author Eric Niebler (eniebler@fb.com)
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <initializer_list>
+#include <new>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+#include <folly/Likely.h>
+#include <folly/Portability.h>
+#include <folly/Preprocessor.h>
+#include <folly/Traits.h> // for construct_in_place_t
+#include <folly/Unit.h>
+
+#define FOLLY_EXPECTED_ID(X) FB_CONCATENATE(FB_CONCATENATE(Folly, X), __LINE__)
+
+#define FOLLY_REQUIRES_IMPL(...) \
+ bool FOLLY_EXPECTED_ID(Requires) = false, \
+ typename std::enable_if< \
+ (FOLLY_EXPECTED_ID(Requires) || static_cast<bool>(__VA_ARGS__)), \
+ int>::type = 0
+
+#define FOLLY_REQUIRES_TRAILING(...) , FOLLY_REQUIRES_IMPL(__VA_ARGS__)
+
+#define FOLLY_REQUIRES(...) template <FOLLY_REQUIRES_IMPL(__VA_ARGS__)>
+
+/**
+ * gcc-4.7 warns about use of uninitialized memory around the use of storage_
+ * even though this is explicitly initialized at each point.
+ */
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif // __GNUC__
+
+namespace folly {
+
+/**
+ * Forward declarations
+ */
+template <class Error>
+class Unexpected;
+
+template <class Error>
+constexpr Unexpected<typename std::decay<Error>::type> makeUnexpected(Error&&);
+
+template <class Value, class Error>
+class Expected;
+
+template <class Error, class Value>
+constexpr Expected<typename std::decay<Value>::type, Error> makeExpected(
+ Value&&);
+
+/**
+ * Alias for an Expected type's assiciated value_type
+ */
+template <class Expected>
+using ExpectedValueType =
+ typename std::remove_reference<Expected>::type::value_type;
+
+/**
+ * Alias for an Expected type's assiciated error_type
+ */
+template <class Expected>
+using ExpectedErrorType =
+ typename std::remove_reference<Expected>::type::error_type;
+
+// Details...
+namespace expected_detail {
+template <template <class...> class Trait, class... Ts>
+using StrictAllOf = StrictConjunction<Trait<Ts>...>;
+
+template <class T>
+using IsCopyable = StrictConjunction<
+ std::is_copy_constructible<T>,
+ std::is_copy_assignable<T>>;
+
+template <class T>
+using IsMovable = StrictConjunction<
+ std::is_move_constructible<T>,
+ std::is_move_assignable<T>>;
+
+template <class T>
+using IsNothrowCopyable = StrictConjunction<
+ std::is_nothrow_copy_constructible<T>,
+ std::is_nothrow_copy_assignable<T>>;
+
+template <class T>
+using IsNothrowMovable = StrictConjunction<
+ std::is_nothrow_move_constructible<T>,
+ std::is_nothrow_move_assignable<T>>;
+
+template <class From, class To>
+using IsConvertible = StrictConjunction<
+ std::is_constructible<To, From>,
+ std::is_assignable<To&, From>>;
+
+template <class T, class U>
+auto doEmplaceAssign(int, T& t, U&& u) -> decltype(void(t = (U &&)u)) {
+ t = (U &&)u;
+}
+
+template <class T, class U>
+auto doEmplaceAssign(long, T& t, U&& u) -> decltype(void(T((U &&)u))) {
+ t.~T();
+ ::new ((void*)std::addressof(t)) T((U &&)u);
+}
+
+template <class T, class... Us>
+auto doEmplaceAssign(int, T& t, Us&&... us)
+ -> decltype(void(t = T((Us &&)us...))) {
+ t = T((Us &&)us...);
+}
+
+template <class T, class... Us>
+auto doEmplaceAssign(long, T& t, Us&&... us)
+ -> decltype(void(T((Us &&)us...))) {
+ t.~T();
+ ::new ((void*)std::addressof(t)) T((Us &&)us...);
+}
+
+struct EmptyTag {};
+struct ValueTag {};
+struct ErrorTag {};
+enum class Which : unsigned char { eEmpty, eValue, eError };
+enum class StorageType { ePODStruct, ePODUnion, eUnion };
+
+template <class Value, class Error>
+constexpr StorageType getStorageType() {
+ return StrictAllOf<IsTriviallyCopyable, Value, Error>::value
+ ? (sizeof(std::pair<Value, Error>) <= sizeof(void * [2]) &&
+ StrictAllOf<std::is_trivial, Value, Error>::value
+ ? StorageType::ePODStruct
+ : StorageType::ePODUnion)
+ : StorageType::eUnion;
+}
+
+template <
+ class Value,
+ class Error,
+ StorageType = expected_detail::getStorageType<Value, Error>()> // ePODUnion
+struct ExpectedStorage {
+ using value_type = Value;
+ using error_type = Error;
+ union {
+ Value value_;
+ Error error_;
+ char ch_;
+ };
+ Which which_;
+
+ template <class E = Error, class = decltype(E())>
+ constexpr ExpectedStorage() noexcept(noexcept(E()))
+ : error_(), which_(Which::eError) {}
+ explicit constexpr ExpectedStorage(EmptyTag) noexcept
+ : ch_(), which_(Which::eEmpty) {}
+ template <class... Vs>
+ explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(
+ noexcept(Value(static_cast<Vs&&>(vs)...)))
+ : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {}
+ template <class... Es>
+ explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept(
+ noexcept(Error(static_cast<Es&&>(es)...)))
+ : error_(static_cast<Es&&>(es)...), which_(Which::eError) {}
+ void clear() noexcept {}
+ static constexpr bool uninitializedByException() noexcept {
+ // Although which_ may temporarily be eEmpty during construction, it
+ // is always either eValue or eError for a fully-constructed Expected.
+ return false;
+ }
+ template <class... Vs>
+ void assignValue(Vs&&... vs) {
+ expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...);
+ which_ = Which::eValue;
+ }
+ template <class... Es>
+ void assignError(Es&&... es) {
+ expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...);
+ which_ = Which::eError;
+ }
+ template <class Other>
+ void assign(Other&& that) {
+ switch (that.which_) {
+ case Which::eValue:
+ this->assignValue(static_cast<Other&&>(that).value());
+ break;
+ case Which::eError:
+ this->assignError(static_cast<Other&&>(that).error());
+ break;
+ default:
+ this->clear();
+ break;
+ }
+ }
+ Value& value() & {
+ return value_;
+ }
+ const Value& value() const& {
+ return value_;
+ }
+ Value&& value() && {
+ return std::move(value_);
+ }
+ Error& error() & {
+ return error_;
+ }
+ const Error& error() const& {
+ return error_;
+ }
+ Error&& error() && {
+ return std::move(error_);
+ }
+};
+
+template <class Value, class Error>
+struct ExpectedUnion {
+ union {
+ Value value_;
+ Error error_;
+ char ch_;
+ };
+ Which which_;
+
+ explicit constexpr ExpectedUnion(EmptyTag = {}) noexcept
+ : ch_(), which_(Which::eEmpty) {}
+ template <class... Vs>
+ explicit constexpr ExpectedUnion(ValueTag, Vs&&... vs) noexcept(
+ noexcept(Value(static_cast<Vs&&>(vs)...)))
+ : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {}
+ template <class... Es>
+ explicit constexpr ExpectedUnion(ErrorTag, Es&&... es) noexcept(
+ noexcept(Error(static_cast<Es&&>(es)...)))
+ : error_(static_cast<Es&&>(es)...), which_(Which::eError) {}
+ ExpectedUnion(const ExpectedUnion&) {}
+ ExpectedUnion(ExpectedUnion&&) noexcept {}
+ ExpectedUnion& operator=(const ExpectedUnion&) {
+ return *this;
+ }
+ ExpectedUnion& operator=(ExpectedUnion&&) noexcept {
+ return *this;
+ }
+ ~ExpectedUnion() {}
+ Value& value() & {
+ return value_;
+ }
+ const Value& value() const& {
+ return value_;
+ }
+ Value&& value() && {
+ return std::move(value_);
+ }
+ Error& error() & {
+ return error_;
+ }
+ const Error& error() const& {
+ return error_;
+ }
+ Error&& error() && {
+ return std::move(error_);
+ }
+};
+
+template <class Derived, bool, bool Noexcept>
+struct CopyConstructible {
+ CopyConstructible() = default;
+ CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) {
+ static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
+ }
+ CopyConstructible(CopyConstructible&&) = default;
+ CopyConstructible& operator=(const CopyConstructible&) = default;
+ CopyConstructible& operator=(CopyConstructible&&) = default;
+};
+
+template <class Derived, bool Noexcept>
+struct CopyConstructible<Derived, false, Noexcept> {
+ CopyConstructible() = default;
+ CopyConstructible(const CopyConstructible&) = delete;
+ CopyConstructible(CopyConstructible&&) = default;
+ CopyConstructible& operator=(const CopyConstructible&) = default;
+ CopyConstructible& operator=(CopyConstructible&&) = default;
+};
+
+template <class Derived, bool, bool Noexcept>
+struct MoveConstructible {
+ MoveConstructible() = default;
+ MoveConstructible(const MoveConstructible&) = default;
+ MoveConstructible(MoveConstructible&& that) noexcept(Noexcept) {
+ static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
+ }
+ MoveConstructible& operator=(const MoveConstructible&) = default;
+ MoveConstructible& operator=(MoveConstructible&&) = default;
+};
+
+template <class Derived, bool Noexcept>
+struct MoveConstructible<Derived, false, Noexcept> {
+ MoveConstructible() = default;
+ MoveConstructible(const MoveConstructible&) = default;
+ MoveConstructible(MoveConstructible&&) = delete;
+ MoveConstructible& operator=(const MoveConstructible&) = default;
+ MoveConstructible& operator=(MoveConstructible&&) = default;
+};
+
+template <class Derived, bool, bool Noexcept>
+struct CopyAssignable {
+ CopyAssignable() = default;
+ CopyAssignable(const CopyAssignable&) = default;
+ CopyAssignable(CopyAssignable&&) = default;
+ CopyAssignable& operator=(const CopyAssignable& that) noexcept(Noexcept) {
+ static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
+ return *this;
+ }
+ CopyAssignable& operator=(CopyAssignable&&) = default;
+};
+
+template <class Derived, bool Noexcept>
+struct CopyAssignable<Derived, false, Noexcept> {
+ CopyAssignable() = default;
+ CopyAssignable(const CopyAssignable&) = default;
+ CopyAssignable(CopyAssignable&&) = default;
+ CopyAssignable& operator=(const CopyAssignable&) = delete;
+ CopyAssignable& operator=(CopyAssignable&&) = default;
+};
+
+template <class Derived, bool, bool Noexcept>
+struct MoveAssignable {
+ MoveAssignable() = default;
+ MoveAssignable(const MoveAssignable&) = default;
+ MoveAssignable(MoveAssignable&&) = default;
+ MoveAssignable& operator=(const MoveAssignable&) = default;
+ MoveAssignable& operator=(MoveAssignable&& that) noexcept(Noexcept) {
+ static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
+ return *this;
+ }
+};
+
+template <class Derived, bool Noexcept>
+struct MoveAssignable<Derived, false, Noexcept> {
+ MoveAssignable() = default;
+ MoveAssignable(const MoveAssignable&) = default;
+ MoveAssignable(MoveAssignable&&) = default;
+ MoveAssignable& operator=(const MoveAssignable&) = default;
+ MoveAssignable& operator=(MoveAssignable&& that) = delete;
+};
+
+template <class Value, class Error>
+struct ExpectedStorage<Value, Error, StorageType::eUnion>
+ : ExpectedUnion<Value, Error>,
+ CopyConstructible<
+ ExpectedStorage<Value, Error, StorageType::eUnion>,
+ StrictAllOf<std::is_copy_constructible, Value, Error>::value,
+ StrictAllOf<std::is_nothrow_copy_constructible, Value, Error>::value>,
+ MoveConstructible<
+ ExpectedStorage<Value, Error, StorageType::eUnion>,
+ StrictAllOf<std::is_move_constructible, Value, Error>::value,
+ StrictAllOf<std::is_nothrow_move_constructible, Value, Error>::value>,
+ CopyAssignable<
+ ExpectedStorage<Value, Error, StorageType::eUnion>,
+ StrictAllOf<IsCopyable, Value, Error>::value,
+ StrictAllOf<IsNothrowCopyable, Value, Error>::value>,
+ MoveAssignable<
+ ExpectedStorage<Value, Error, StorageType::eUnion>,
+ StrictAllOf<IsMovable, Value, Error>::value,
+ StrictAllOf<IsNothrowMovable, Value, Error>::value> {
+ using value_type = Value;
+ using error_type = Error;
+ using Base = ExpectedUnion<Value, Error>;
+ template <class E = Error, class = decltype(E())>
+ constexpr ExpectedStorage() noexcept(noexcept(E())) : Base{ErrorTag{}} {}
+ ExpectedStorage(const ExpectedStorage&) = default;
+ ExpectedStorage(ExpectedStorage&&) = default;
+ ExpectedStorage& operator=(const ExpectedStorage&) = default;
+ ExpectedStorage& operator=(ExpectedStorage&&) = default;
+ using ExpectedUnion<Value, Error>::ExpectedUnion;
+ ~ExpectedStorage() {
+ clear();
+ }
+ void clear() noexcept {
+ switch (this->which_) {
+ case Which::eValue:
+ this->value().~Value();
+ break;
+ case Which::eError:
+ this->error().~Error();
+ break;
+ default:
+ break;
+ }
+ this->which_ = Which::eEmpty;
+ }
+ bool uninitializedByException() const noexcept {
+ return this->which_ == Which::eEmpty;
+ }
+ template <class... Vs>
+ void assignValue(Vs&&... vs) {
+ if (this->which_ == Which::eValue) {
+ expected_detail::doEmplaceAssign(
+ 0, this->value(), static_cast<Vs&&>(vs)...);
+ } else {
+ this->clear();
+ ::new ((void*)std::addressof(this->value()))
+ Value(static_cast<Vs&&>(vs)...);
+ this->which_ = Which::eValue;
+ }
+ }
+ template <class... Es>
+ void assignError(Es&&... es) {
+ if (this->which_ == Which::eError) {
+ expected_detail::doEmplaceAssign(
+ 0, this->error(), static_cast<Es&&>(es)...);
+ } else {
+ this->clear();
+ ::new ((void*)std::addressof(this->error()))
+ Error(static_cast<Es&&>(es)...);
+ this->which_ = Which::eError;
+ }
+ }
+ bool isThis(const ExpectedStorage* that) const {
+ return this == that;
+ }
+ constexpr bool isSelfAssign(const void*) const {
+ return false;
+ }
+ template <class Other>
+ void assign(Other&& that) {
+ if (isSelfAssign(&that))
+ return;
+ switch (that.which_) {
+ case Which::eValue:
+ this->assignValue(static_cast<Other&&>(that).value());
+ break;
+ case Which::eError:
+ this->assignError(static_cast<Other&&>(that).error());
+ break;
+ default:
+ this->clear();
+ break;
+ }
+ }
+};
+
+// For small (pointer-sized) trivial types, a struct is faster than a union.
+template <class Value, class Error>
+struct ExpectedStorage<Value, Error, StorageType::ePODStruct> {
+ using value_type = Value;
+ using error_type = Error;
+ Which which_;
+ Error error_;
+ Value value_;
+
+ constexpr ExpectedStorage() noexcept
+ : which_(Which::eError), error_(), value_() {}
+ explicit constexpr ExpectedStorage(EmptyTag) noexcept
+ : which_(Which::eEmpty), error_(), value_() {}
+ template <class... Vs>
+ explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(
+ noexcept(Value(static_cast<Vs&&>(vs)...)))
+ : which_(Which::eValue), error_(), value_(static_cast<Vs&&>(vs)...) {}
+ template <class... Es>
+ explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept(
+ noexcept(Error(static_cast<Es&&>(es)...)))
+ : which_(Which::eError), error_(static_cast<Es&&>(es)...), value_() {}
+ void clear() noexcept {}
+ constexpr static bool uninitializedByException() noexcept {
+ return false;
+ }
+ template <class... Vs>
+ void assignValue(Vs&&... vs) {
+ expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...);
+ which_ = Which::eValue;
+ }
+ template <class... Es>
+ void assignError(Es&&... es) {
+ expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...);
+ which_ = Which::eError;
+ }
+ template <class Other>
+ void assign(Other&& that) {
+ switch (that.which_) {
+ case Which::eValue:
+ this->assignValue(static_cast<Other&&>(that).value());
+ break;
+ case Which::eError:
+ this->assignError(static_cast<Other&&>(that).error());
+ break;
+ default:
+ this->clear();
+ break;
+ }
+ }
+ Value& value() & {
+ return value_;
+ }
+ const Value& value() const& {
+ return value_;
+ }
+ Value&& value() && {
+ return std::move(value_);
+ }
+ Error& error() & {
+ return error_;
+ }
+ const Error& error() const& {
+ return error_;
+ }
+ Error&& error() && {
+ return std::move(error_);
+ }
+};
+
+namespace expected_detail_ExpectedHelper {
+// Tricky hack so that Expected::then can handle lambdas that return void
+template <class T>
+inline T&& operator,(T&& t, Unit) noexcept {
+ return static_cast<T&&>(t);
+}
+
+struct ExpectedHelper {
+ template <class Error, class T>
+ static constexpr Expected<T, Error> return_(T t) {
+ return folly::makeExpected<Error>(t);
+ }
+ template <
+ class Error,
+ class T,
+ class U FOLLY_REQUIRES_TRAILING(
+ expected_detail::IsConvertible<U&&, Error>::value)>
+ static constexpr Expected<T, Error> return_(Expected<T, U> t) {
+ return t;
+ }
+
+ template <class This>
+ static typename std::decay<This>::type then_(This&& ex) {
+ return static_cast<This&&>(ex);
+ }
+
+ template <
+ class This,
+ class Fn,
+ class... Fns,
+ class E = ExpectedErrorType<This>,
+ class T = ExpectedHelper>
+ static auto then_(This&& ex, Fn&& fn, Fns&&... fns) -> decltype(T::then_(
+ T::template return_<E>(
+ (std::declval<Fn>()(std::declval<This>().value()), unit)),
+ std::declval<Fns>()...)) {
+ if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+ return T::then_(
+ T::template return_<E>(
+ // Uses the comma operator defined above IFF the lambda
+ // returns non-void.
+ (static_cast<Fn&&>(fn)(static_cast<This&&>(ex).value()), unit)),
+ static_cast<Fns&&>(fns)...);
+ return makeUnexpected(static_cast<This&&>(ex).error());
+ }
+
+ template <
+ class This,
+ class Yes,
+ class No,
+ class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())),
+ class Err = decltype(std::declval<No>()(std::declval<This>().error()))
+ FOLLY_REQUIRES_TRAILING(!std::is_void<Err>::value)>
+ static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {
+ if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+ return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));
+ throw static_cast<No&&>(no)(static_cast<This&&>(ex).error());
+ }
+
+ template <
+ class This,
+ class Yes,
+ class No,
+ class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())),
+ class Err = decltype(std::declval<No>()(std::declval<This&>().error()))
+ FOLLY_REQUIRES_TRAILING(std::is_void<Err>::value)>
+ static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {
+ if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+ return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));
+ static_cast<No&&>(no)(ex.error());
+ throw typename Unexpected<ExpectedErrorType<This>>::MakeBadExpectedAccess()(
+ static_cast<This&&>(ex).error());
+ }
+};
+}
+/* using override */ using expected_detail_ExpectedHelper::ExpectedHelper;
+
+struct UnexpectedTag {};
+
+} // namespace expected_detail
+
+using unexpected_t =
+ expected_detail::UnexpectedTag (&)(expected_detail::UnexpectedTag);
+
+inline expected_detail::UnexpectedTag unexpected(
+ expected_detail::UnexpectedTag = {}) {
+ return {};
+}
+
+/**
+ * An exception type thrown by Expected on catastrophic logic errors.
+ */
+class BadExpectedAccess : public std::logic_error {
+ public:
+ BadExpectedAccess() : std::logic_error("bad Expected access") {}
+};
+
+/**
+ * Unexpected - a helper type used to disambiguate the construction of
+ * Expected objects in the error state.
+ */
+template <class Error>
+class Unexpected final {
+ template <class E>
+ friend class Unexpected;
+ template <class V, class E>
+ friend class Expected;
+ friend struct expected_detail::ExpectedHelper;
+
+ public:
+ /**
+ * Unexpected::BadExpectedAccess - An exception type thrown by Expected
+ * when the user tries to access the nested value but the Expected object is
+ * actually storing an error code.
+ */
+ class BadExpectedAccess : public folly::BadExpectedAccess {
+ public:
+ explicit BadExpectedAccess(Error err)
+ : folly::BadExpectedAccess(), error_(std::move(err)) {}
+ /**
+ * The error code that was held by the Expected object when the user
+ * erroneously requested the value.
+ */
+ Error error() const {
+ return error_;
+ }
+
+ private:
+ Error error_;
+ };
+
+ /**
+ * Constructors
+ */
+ Unexpected() = default;
+ Unexpected(const Unexpected&) = default;
+ Unexpected(Unexpected&&) = default;
+ Unexpected& operator=(const Unexpected&) = default;
+ Unexpected& operator=(Unexpected&&) = default;
+ constexpr /* implicit */ Unexpected(const Error& err) : error_(err) {}
+ constexpr /* implicit */ Unexpected(Error&& err) : error_(std::move(err)) {}
+
+ template <class Other FOLLY_REQUIRES_TRAILING(
+ std::is_constructible<Error, Other&&>::value)>
+ constexpr /* implicit */ Unexpected(Unexpected<Other> that)
+ : error_(std::move(that.error())) {}
+
+ /**
+ * Assignment
+ */
+ template <class Other FOLLY_REQUIRES_TRAILING(
+ std::is_assignable<Error&, Other&&>::value)>
+ Unexpected& operator=(Unexpected<Other> that) {
+ error_ = std::move(that.error());
+ }
+
+ /**
+ * Observers
+ */
+ Error& error() & {
+ return error_;
+ }
+ const Error& error() const& {
+ return error_;
+ }
+ Error&& error() && {
+ return std::move(error_);
+ }
+
+ /**
+ * Relational operators
+ */
+ FOLLY_REQUIRES(IsEqualityComparable<Error>::value)
+ friend bool operator==(const Unexpected& lhs, const Unexpected& rhs) {
+ return lhs.error() == rhs.error();
+ }
+ FOLLY_REQUIRES(IsEqualityComparable<Error>::value)
+ friend bool operator!=(const Unexpected& lhs, const Unexpected& rhs) {
+ return !(lhs == rhs);
+ }
+
+ private:
+ struct MakeBadExpectedAccess {
+ template <class E>
+ BadExpectedAccess operator()(E&& err) const {
+ return BadExpectedAccess(static_cast<E&&>(err));
+ }
+ };
+
+ Error error_;
+};
+
+/**
+ * For constructing an Unexpected object from an error code. Unexpected objects
+ * are implicitly convertible to Expected object in the error state. Usage is
+ * as follows:
+ *
+ * enum class MyErrorCode { BAD_ERROR, WORSE_ERROR };
+ * Expected<int, MyErrorCode> myAPI() {
+ * int i = // ...;
+ * return i ? makeExpected<MyErrorCode>(i)
+ * : makeUnexpected(MyErrorCode::BAD_ERROR);
+ * }
+ */
+template <class Error>
+constexpr Unexpected<typename std::decay<Error>::type> makeUnexpected(
+ Error&& err) {
+ return Unexpected<typename std::decay<Error>::type>{
+ static_cast<Error&&>(err)};
+}
+
+/**
+ * Expected - For holding a value or an error. Useful as an alternative to
+ * exceptions, for APIs where throwing on failure would be too expensive.
+ *
+ * Expected<Value, Error> is a variant over the types Value and Error.
+ *
+ * Expected does not offer support for references. Use
+ * Expected<std::reference_wrapper<T>, Error> if your API needs to return a
+ * reference or an error.
+ *
+ * Expected offers a continuation-based interface to reduce the boilerplate
+ * of checking error codes. The Expected::then member function takes a lambda
+ * that is to execute should the Expected object contain a value. The return
+ * value of the lambda is wrapped in an Expected and returned. If the lambda is
+ * not executed because the Expected contains an error, the error is returned
+ * immediately in a new Expected object.
+ *
+ * Expected<int, Error> funcTheFirst();
+ * Expected<std::string, Error> funcTheSecond() {
+ * return funcTheFirst().then([](int i) { return std::to_string(i); });
+ * }
+ *
+ * The above line of code could more verbosely written as:
+ *
+ * Expected<std::string, Error> funcTheSecond() {
+ * if (auto ex = funcTheFirst()) {
+ * return std::to_string(*ex);
+ * }
+ * return makeUnexpected(ex.error());
+ * }
+ *
+ * Continuations can chain, like:
+ *
+ * Expected<D, Error> maybeD = someFunc()
+ * .then([](A a){return B(a);})
+ * .then([](B b){return C(b);})
+ * .then([](C c){return D(c);});
+ *
+ * To avoid the redundant error checking that would happen if a call at the
+ * front of the chain returns an error, these call chains can be collaped into
+ * a single call to .then:
+ *
+ * Expected<D, Error> maybeD = someFunc()
+ * .then([](A a){return B(a);},
+ * [](B b){return C(b);},
+ * [](C c){return D(c);});
+ *
+ * The result of .then() is wrapped into Expected< ~, Error > if it isn't
+ * of that form already. Consider the following code:
+ *
+ * extern Expected<std::string, Error> readLineFromIO();
+ * extern Expected<int, Error> parseInt(std::string);
+ * extern int increment(int);
+ *
+ * Expected<int, Error> x = readLineFromIO().then(parseInt).then(increment);
+ *
+ * From the code above, we see that .then() works both with functions that
+ * return an Expected< ~, Error > (like parseInt) and with ones that return
+ * a plain value (like increment). In the case of parseInt, .then() returns
+ * the result of parseInt as-is. In the case of increment, it wraps the int
+ * that increment returns into an Expected< int, Error >.
+ *
+ * Sometimes when using a continuation you would prefer an exception to be
+ * thrown for a value-less Expected. For that you can use .thenOrThrow, as
+ * follows:
+ *
+ * B b = someFunc()
+ * .thenOrThrow([](A a){return B(a);});
+ *
+ * The above call to thenOrThrow will invoke the lambda if the Expected returned
+ * by someFunc() contains a value. Otherwise, it will throw an exception of type
+ * Unexpected<Error>::BadExpectedAccess. If you prefer it throw an exception of
+ * a different type, you can pass a second lambda to thenOrThrow:
+ *
+ * B b = someFunc()
+ * .thenOrThrow([](A a){return B(a);},
+ * [](Error e) {throw MyException(e);});
+ *
+ * Like C++17's std::variant, Expected offers the almost-never-empty guarantee;
+ * that is, an Expected<Value, Error> almost always contains either a Value or
+ * and Error. Partially-formed Expected objects occur when an assignment to
+ * an Expected object that would change the type of the contained object (Value-
+ * to-Error or vice versa) throws. Trying to access either the contained value
+ * or error object causes Expected to throw folly::BadExpectedAccess.
+ *
+ * Expected models OptionalPointee, so calling 'get_pointer(ex)' will return a
+ * pointer to nullptr if the 'ex' is in the error state, and a pointer to the
+ * value otherwise:
+ *
+ * Expected<int, Error> maybeInt = ...;
+ * if (int* v = get_pointer(maybeInt)) {
+ * cout << *v << endl;
+ * }
+ */
+template <class Value, class Error>
+class Expected final : expected_detail::ExpectedStorage<Value, Error> {
+ template <class, class>
+ friend class Expected;
+ template <class, class, expected_detail::StorageType>
+ friend struct expected_detail::ExpectedStorage;
+ friend struct expected_detail::ExpectedHelper;
+ using Base = expected_detail::ExpectedStorage<Value, Error>;
+ using MakeBadExpectedAccess =
+ typename Unexpected<Error>::MakeBadExpectedAccess;
+ Base& base() & {
+ return *this;
+ }
+ const Base& base() const& {
+ return *this;
+ }
+ Base&& base() && {
+ return std::move(*this);
+ }
+
+ public:
+ using value_type = Value;
+ using error_type = Error;
+ using IsTriviallyCopyable = typename expected_detail::
+ StrictAllOf<IsTriviallyCopyable, Value, Error>::type;
+
+ template <class U>
+ using rebind = Expected<U, Error>;
+
+ static_assert(
+ !std::is_reference<Value>::value,
+ "Expected may not be used with reference types");
+ static_assert(
+ !std::is_abstract<Value>::value,
+ "Expected may not be used with abstract types");
+
+ /*
+ * Constructors
+ */
+ template <class B = Base, class = decltype(B{})>
+ Expected() noexcept(noexcept(B{})) : Base() {}
+ Expected(const Expected& that) = default;
+ Expected(Expected&& that) = default;
+
+ template <
+ class V,
+ class E FOLLY_REQUIRES_TRAILING(
+ !std::is_same<Expected<V, E>, Expected>::value &&
+ std::is_constructible<Value, V&&>::value &&
+ std::is_constructible<Error, E&&>::value)>
+ Expected(Expected<V, E> that) : Base{expected_detail::EmptyTag{}} {
+ *this = std::move(that);
+ }
+
+ FOLLY_REQUIRES(std::is_copy_constructible<Value>::value)
+ constexpr /* implicit */ Expected(const Value& val) noexcept(
+ noexcept(Value(val)))
+ : Base{expected_detail::ValueTag{}, val} {}
+
+ FOLLY_REQUIRES(std::is_move_constructible<Value>::value)
+ constexpr /* implicit */ Expected(Value&& val) noexcept(
+ noexcept(Value(std::move(val))))
+ : Base{expected_detail::ValueTag{}, std::move(val)} {}
+
+ template <class T FOLLY_REQUIRES_TRAILING(
+ std::is_convertible<T, Value>::value &&
+ !std::is_convertible<T, Error>::value)>
+ constexpr /* implicit */ Expected(T&& val) noexcept(
+ noexcept(Value(static_cast<T&&>(val))))
+ : Base{expected_detail::ValueTag{}, static_cast<T&&>(val)} {}
+
+ template <class... Ts FOLLY_REQUIRES_TRAILING(
+ std::is_constructible<Value, Ts&&...>::value)>
+ explicit constexpr Expected(in_place_t, Ts&&... ts) noexcept(
+ noexcept(Value(std::declval<Ts>()...)))
+ : Base{expected_detail::ValueTag{}, static_cast<Ts&&>(ts)...} {}
+
+ template <
+ class U,
+ class... Ts FOLLY_REQUIRES_TRAILING(
+ std::is_constructible<Value, std::initializer_list<U>&, Ts&&...>::
+ value)>
+ explicit constexpr Expected(
+ in_place_t,
+ std::initializer_list<U> il,
+ Ts&&... ts) noexcept(noexcept(Value(std::declval<Ts>()...)))
+ : Base{expected_detail::ValueTag{}, il, static_cast<Ts&&>(ts)...} {}
+
+ // If overload resolution selects one of these deleted functions, that
+ // means you need to use makeUnexpected
+ /* implicit */ Expected(const Error&) = delete;
+ /* implicit */ Expected(Error&&) = delete;
+
+ FOLLY_REQUIRES(std::is_copy_constructible<Error>::value)
+ constexpr Expected(unexpected_t, const Error& err) noexcept(
+ noexcept(Error(err)))
+ : Base{expected_detail::ErrorTag{}, err} {}
+
+ FOLLY_REQUIRES(std::is_move_constructible<Error>::value)
+ constexpr Expected(unexpected_t, Error&& err) noexcept(
+ noexcept(Error(std::move(err))))
+ : Base{expected_detail::ErrorTag{}, std::move(err)} {}
+
+ FOLLY_REQUIRES(std::is_copy_constructible<Error>::value)
+ constexpr /* implicit */ Expected(const Unexpected<Error>& err) noexcept(
+ noexcept(Error(err.error())))
+ : Base{expected_detail::ErrorTag{}, err.error()} {}
+
+ FOLLY_REQUIRES(std::is_move_constructible<Error>::value)
+ constexpr /* implicit */ Expected(Unexpected<Error>&& err) noexcept(
+ noexcept(Error(std::move(err.error()))))
+ : Base{expected_detail::ErrorTag{}, std::move(err.error())} {}
+
+ /*
+ * Assignment operators
+ */
+ Expected& operator=(const Expected& that) = default;
+ Expected& operator=(Expected&& that) = default;
+
+ template <
+ class V,
+ class E FOLLY_REQUIRES_TRAILING(
+ !std::is_same<Expected<V, E>, Expected>::value &&
+ expected_detail::IsConvertible<V&&, Value>::value &&
+ expected_detail::IsConvertible<E&&, Error>::value)>
+ Expected& operator=(Expected<V, E> that) {
+ this->assign(std::move(that));
+ return *this;
+ }
+
+ FOLLY_REQUIRES(expected_detail::IsCopyable<Value>::value)
+ Expected& operator=(const Value& val) noexcept(
+ expected_detail::IsNothrowCopyable<Value>::value) {
+ this->assignValue(val);
+ return *this;
+ }
+
+ FOLLY_REQUIRES(expected_detail::IsMovable<Value>::value)
+ Expected& operator=(Value&& val) noexcept(
+ expected_detail::IsNothrowMovable<Value>::value) {
+ this->assignValue(std::move(val));
+ return *this;
+ }
+
+ template <class T FOLLY_REQUIRES_TRAILING(
+ std::is_convertible<T, Value>::value &&
+ !std::is_convertible<T, Error>::value)>
+ Expected& operator=(T&& val) {
+ this->assignValue(static_cast<T&&>(val));
+ return *this;
+ }
+
+ FOLLY_REQUIRES(expected_detail::IsCopyable<Error>::value)
+ Expected& operator=(const Unexpected<Error>& err) noexcept(
+ expected_detail::IsNothrowCopyable<Error>::value) {
+ this->assignError(err.error());
+ return *this;
+ }
+
+ FOLLY_REQUIRES(expected_detail::IsMovable<Error>::value)
+ Expected& operator=(Unexpected<Error>&& err) noexcept(
+ expected_detail::IsNothrowMovable<Error>::value) {
+ this->assignError(std::move(err.error()));
+ return *this;
+ }
+
+ template <class... Ts FOLLY_REQUIRES_TRAILING(
+ std::is_constructible<Value, Ts&&...>::value)>
+ void emplace(Ts&&... ts) {
+ this->assignValue(static_cast<Ts&&>(ts)...);
+ }
+
+ /**
+ * swap
+ */
+ void swap(Expected& that) noexcept(
+ expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) {
+ if (this->uninitializedByException() || that.uninitializedByException())
+ throw BadExpectedAccess();
+ using std::swap;
+ if (*this) {
+ if (that) {
+ swap(this->value_, that.value_);
+ } else {
+ Error e(std::move(that.error_));
+ that.assignValue(std::move(this->value_));
+ this->assignError(std::move(e));
+ }
+ } else {
+ if (!that) {
+ swap(this->error_, that.error_);
+ } else {
+ Error e(std::move(this->error_));
+ this->assignValue(std::move(that.value_));
+ that.assignError(std::move(e));
+ }
+ }
+ }
+
+ // If overload resolution selects one of these deleted functions, that
+ // means you need to use makeUnexpected
+ /* implicit */ Expected& operator=(const Error&) = delete;
+ /* implicit */ Expected& operator=(Error&&) = delete;
+
+ /**
+ * Relational Operators
+ */
+ FOLLY_REQUIRES(IsEqualityComparable<Value>::value)
+ friend bool operator==(const Expected& lhs, const Expected& rhs) {
+ if (UNLIKELY(lhs.which_ != rhs.which_))
+ return UNLIKELY(lhs.uninitializedByException())
+ ? false
+ : throw BadExpectedAccess();
+ if (UNLIKELY(lhs.uninitializedByException()))
+ throw BadExpectedAccess();
+ if (UNLIKELY(lhs.hasError()))
+ return true; // All error states are considered equal
+ return lhs.value_ == rhs.value_;
+ }
+
+ FOLLY_REQUIRES(IsEqualityComparable<Value>::value)
+ friend bool operator!=(const Expected& lhs, const Expected& rhs) {
+ return !(lhs == rhs);
+ }
+
+ FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+ friend bool operator<(const Expected& lhs, const Expected& rhs) {
+ if (UNLIKELY(
+ lhs.uninitializedByException() || rhs.uninitializedByException()))
+ throw BadExpectedAccess();
+ if (UNLIKELY(lhs.hasError()))
+ return !rhs.hasError();
+ if (UNLIKELY(rhs.hasError()))
+ return false;
+ return lhs.value_ < rhs.value_;
+ }
+
+ FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+ friend bool operator<=(const Expected& lhs, const Expected& rhs) {
+ return !(rhs < lhs);
+ }
+
+ FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+ friend bool operator>(const Expected& lhs, const Expected& rhs) {
+ return rhs < lhs;
+ }
+
+ FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+ friend bool operator>=(const Expected& lhs, const Expected& rhs) {
+ return !(lhs < rhs);
+ }
+
+ /*
+ * Accessors
+ */
+ constexpr bool hasValue() const noexcept {
+ return expected_detail::Which::eValue == this->which_;
+ }
+
+ constexpr bool hasError() const noexcept {
+ return expected_detail::Which::eError == this->which_;
+ }
+
+ using Base::uninitializedByException;
+
+ const Value& value() const& {
+ requireValue();
+ return this->Base::value();
+ }
+
+ Value& value() & {
+ requireValue();
+ return this->Base::value();
+ }
+
+ Value&& value() && {
+ requireValue();
+ return std::move(this->Base::value());
+ }
+
+ const Error& error() const& {
+ requireError();
+ return this->Base::error();
+ }
+
+ Error& error() & {
+ requireError();
+ return this->Base::error();
+ }
+
+ Error&& error() && {
+ requireError();
+ return std::move(this->Base::error());
+ }
+
+ // Return a copy of the value if set, or a given default if not.
+ template <class U>
+ Value value_or(U&& dflt) const& {
+ if (LIKELY(this->which_ == expected_detail::Which::eValue)) {
+ return this->value_;
+ }
+ return static_cast<U&&>(dflt);
+ }
+
+ template <class U>
+ Value value_or(U&& dflt) && {
+ if (LIKELY(this->which_ == expected_detail::Which::eValue)) {
+ return std::move(this->value_);
+ }
+ return static_cast<U&&>(dflt);
+ }
+
+ explicit constexpr operator bool() const noexcept {
+ return hasValue();
+ }
+
+ const Value& operator*() const& {
+ return this->value();
+ }
+
+ Value& operator*() & {
+ return this->value();
+ }
+
+ Value&& operator*() && {
+ return std::move(this->value());
+ }
+
+ const Value* operator->() const {
+ return std::addressof(this->value());
+ }
+
+ Value* operator->() {
+ return std::addressof(this->value());
+ }
+
+ const Value* get_pointer() const& noexcept {
+ return hasValue() ? std::addressof(this->value_) : nullptr;
+ }
+
+ Value* get_pointer() & noexcept {
+ return hasValue() ? std::addressof(this->value_) : nullptr;
+ }
+
+ Value* get_pointer() && = delete;
+
+ /**
+ * then
+ */
+ template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
+ auto then(Fns&&... fns) const& -> decltype(
+ expected_detail::ExpectedHelper::then_(
+ std::declval<const Base&>(),
+ std::declval<Fns>()...)) {
+ if (this->uninitializedByException())
+ throw BadExpectedAccess();
+ return expected_detail::ExpectedHelper::then_(
+ base(), static_cast<Fns&&>(fns)...);
+ }
+
+ template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
+ auto then(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::then_(
+ std::declval<Base&>(),
+ std::declval<Fns>()...)) {
+ if (this->uninitializedByException())
+ throw BadExpectedAccess();
+ return expected_detail::ExpectedHelper::then_(
+ base(), static_cast<Fns&&>(fns)...);
+ }
+
+ template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
+ auto then(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::then_(
+ std::declval<Base&&>(),
+ std::declval<Fns>()...)) {
+ if (this->uninitializedByException())
+ throw BadExpectedAccess();
+ return expected_detail::ExpectedHelper::then_(
+ std::move(base()), static_cast<Fns&&>(fns)...);
+ }
+
+ /**
+ * thenOrThrow
+ */
+ template <class Yes, class No = MakeBadExpectedAccess>
+ auto thenOrThrow(Yes&& yes, No&& no = No()) const& -> decltype(
+ std::declval<Yes>()(std::declval<const Value&>())) {
+ using Ret = decltype(std::declval<Yes>()(std::declval<const Value&>()));
+ if (this->uninitializedByException())
+ throw BadExpectedAccess();
+ return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
+ base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+ }
+
+ template <class Yes, class No = MakeBadExpectedAccess>
+ auto thenOrThrow(Yes&& yes, No&& no = No()) & -> decltype(
+ std::declval<Yes>()(std::declval<Value&>())) {
+ using Ret = decltype(std::declval<Yes>()(std::declval<Value&>()));
+ if (this->uninitializedByException())
+ throw BadExpectedAccess();
+ return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
+ base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+ }
+
+ template <class Yes, class No = MakeBadExpectedAccess>
+ auto thenOrThrow(Yes&& yes, No&& no = No()) && -> decltype(
+ std::declval<Yes>()(std::declval<Value&&>())) {
+ using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>()));
+ if (this->uninitializedByException())
+ throw BadExpectedAccess();
+ return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
+ std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+ }
+
+ private:
+ void requireValue() const {
+ if (UNLIKELY(!hasValue())) {
+ if (LIKELY(hasError()))
+ throw typename Unexpected<Error>::BadExpectedAccess(this->error_);
+ throw BadExpectedAccess();
+ }
+ }
+
+ void requireError() const {
+ if (UNLIKELY(!hasError())) {
+ throw BadExpectedAccess();
+ }
+ }
+
+ expected_detail::Which which() const noexcept {
+ return this->which_;
+ }
+};
+
+/**
+ * swap Expected values
+ */
+template <class Error, class Value>
+void swap(Expected<Error, Value>& lhs, Expected<Value, Error>& rhs) noexcept(
+ expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) {
+ lhs.swap(rhs);
+}
+
+template <class Value, class Error>
+const Value* get_pointer(const Expected<Value, Error>& ex) noexcept {
+ return ex.get_pointer();
+}
+
+template <class Value, class Error>
+Value* get_pointer(Expected<Value, Error>& ex) noexcept {
+ return ex.get_pointer();
+}
+
+/**
+ * For constructing an Expected object from a value, with the specified
+ * Error type. Usage is as follows:
+ *
+ * enum MyErrorCode { BAD_ERROR, WORSE_ERROR };
+ * Expected<int, MyErrorCode> myAPI() {
+ * int i = // ...;
+ * return i ? makeExpected<MyErrorCode>(i) : makeUnexpected(BAD_ERROR);
+ * }
+ */
+template <class Error, class Value>
+constexpr Expected<typename std::decay<Value>::type, Error> makeExpected(
+ Value&& val) {
+ return Expected<typename std::decay<Value>::type, Error>{
+ in_place, static_cast<Value&&>(val)};
+}
+
+// Suppress comparability of Optional<T> with T, despite implicit conversion.
+template <class Value, class Error>
+bool operator==(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator!=(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator<(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator<=(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator>=(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator>(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator==(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator!=(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator<(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator<=(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator>=(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator>(const Value& other, const Expected<Value, Error>&) = delete;
+
+} // namespace folly
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+#undef FOLLY_REQUIRES
+#undef FOLLY_REQUIRES_TRAILING
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/Expected.h>
+#include <folly/Portability.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+using std::unique_ptr;
+using std::shared_ptr;
+
+namespace folly {
+
+enum class E { E1, E2 };
+
+std::ostream& operator<<(std::ostream& os, E e) {
+ switch (e) {
+ case E::E1:
+ return os << "E::E1";
+ case E::E2:
+ return os << "E::E2";
+ default:;
+ }
+ return os;
+}
+
+template <class V, class E>
+std::ostream& operator<<(std::ostream& os, const Expected<V, E>& e) {
+ if (e) {
+ os << "Expected(" << e.value() << ')';
+ } else {
+ os << "Unexpected(" << e.error() << ')';
+ }
+ return os;
+}
+
+struct NoDefault {
+ NoDefault(int, int) {}
+ char a, b, c;
+};
+
+TEST(Expected, NoDefault) {
+ static_assert(
+ std::is_default_constructible<Expected<NoDefault, int>>::value, "");
+ Expected<NoDefault, int> x{in_place, 42, 42};
+ EXPECT_TRUE(x);
+ x.emplace(4, 5);
+ EXPECT_TRUE(bool(x));
+ x = makeUnexpected(42);
+ EXPECT_FALSE(x);
+ EXPECT_EQ(42, x.error());
+}
+
+TEST(Expected, String) {
+ Expected<std::string, int> maybeString;
+ EXPECT_FALSE(maybeString);
+ EXPECT_EQ(0, maybeString.error());
+ maybeString = "hello";
+ EXPECT_TRUE(bool(maybeString));
+ EXPECT_EQ("hello", *maybeString);
+}
+
+TEST(Expected, Ambiguous) {
+ // Potentially ambiguous and confusing construction and assignment disallowed:
+ EXPECT_FALSE((std::is_constructible<Expected<int, int>, int>::value));
+ EXPECT_FALSE((std::is_assignable<Expected<int, int>&, int>::value));
+}
+
+TEST(Expected, Const) {
+ { // default construct
+ Expected<const int, int> ex;
+ EXPECT_FALSE(bool(ex));
+ EXPECT_EQ(0, ex.error());
+ ex.emplace(4);
+ EXPECT_EQ(4, *ex);
+ ex.emplace(5);
+ EXPECT_EQ(5, *ex);
+ ex = makeUnexpected(42);
+ EXPECT_FALSE(bool(ex));
+ EXPECT_EQ(42, ex.error());
+ }
+ { // copy-constructed
+ const int x = 6;
+ Expected<const int, int> ex{in_place, x};
+ Expected<const int, int> ex2 = ex;
+ EXPECT_EQ(6, *ex2);
+ }
+ { // move-constructed
+ const int x = 7;
+ Expected<const int, int> ex{in_place, std::move(x)};
+ Expected<const int, int> ex2 = std::move(ex);
+ EXPECT_EQ(7, *ex2);
+ }
+ // no assignment allowed
+ EXPECT_FALSE((std::is_copy_assignable<Expected<const int, int>>::value));
+}
+
+TEST(Expected, Simple) {
+ Expected<int, int> ex;
+ EXPECT_FALSE(bool(ex));
+ EXPECT_EQ(42, ex.value_or(42));
+ ex.emplace(4);
+ EXPECT_TRUE(bool(ex));
+ EXPECT_EQ(4, *ex);
+ EXPECT_EQ(4, ex.value_or(42));
+ ex = makeUnexpected(-1);
+ EXPECT_FALSE(bool(ex));
+ EXPECT_EQ(-1, ex.error());
+ EXPECT_EQ(42, ex.value_or(42));
+}
+
+class MoveTester {
+ public:
+ /* implicit */ MoveTester(const char* s) : s_(s) {}
+ MoveTester(const MoveTester&) = default;
+ MoveTester(MoveTester&& other) noexcept {
+ s_ = std::move(other.s_);
+ other.s_ = "";
+ }
+ MoveTester& operator=(const MoveTester&) = default;
+ MoveTester& operator=(MoveTester&& other) noexcept {
+ s_ = std::move(other.s_);
+ other.s_ = "";
+ return *this;
+ }
+
+ private:
+ friend bool operator==(const MoveTester& o1, const MoveTester& o2);
+ std::string s_;
+};
+
+bool operator==(const MoveTester& o1, const MoveTester& o2) {
+ return o1.s_ == o2.s_;
+}
+
+TEST(Expected, value_or_rvalue_arg) {
+ Expected<MoveTester, int> ex = makeUnexpected(-1);
+ MoveTester dflt = "hello";
+ EXPECT_EQ("hello", ex.value_or(dflt));
+ EXPECT_EQ("hello", dflt);
+ EXPECT_EQ("hello", ex.value_or(std::move(dflt)));
+ EXPECT_EQ("", dflt);
+ EXPECT_EQ("world", ex.value_or("world"));
+
+ dflt = "hello";
+ // Make sure that the const overload works on const objects
+ const auto& exc = ex;
+ EXPECT_EQ("hello", exc.value_or(dflt));
+ EXPECT_EQ("hello", dflt);
+ EXPECT_EQ("hello", exc.value_or(std::move(dflt)));
+ EXPECT_EQ("", dflt);
+ EXPECT_EQ("world", exc.value_or("world"));
+
+ dflt = "hello";
+ ex = "meow";
+ EXPECT_EQ("meow", ex.value_or(dflt));
+ EXPECT_EQ("hello", dflt);
+ EXPECT_EQ("meow", ex.value_or(std::move(dflt)));
+ EXPECT_EQ("hello", dflt); // only moved if used
+}
+
+TEST(Expected, value_or_noncopyable) {
+ Expected<std::unique_ptr<int>, int> ex{unexpected, 42};
+ std::unique_ptr<int> dflt(new int(42));
+ EXPECT_EQ(42, *std::move(ex).value_or(std::move(dflt)));
+}
+
+struct ExpectingDeleter {
+ explicit ExpectingDeleter(int expected) : expected(expected) {}
+ int expected;
+ void operator()(const int* ptr) {
+ EXPECT_EQ(*ptr, expected);
+ delete ptr;
+ }
+};
+
+TEST(Expected, value_move) {
+ auto ptr = Expected<std::unique_ptr<int, ExpectingDeleter>, int>(
+ in_place, new int(42), ExpectingDeleter{1337})
+ .value();
+ *ptr = 1337;
+}
+
+TEST(Expected, dereference_move) {
+ auto ptr = *Expected<std::unique_ptr<int, ExpectingDeleter>, int>(
+ in_place, new int(42), ExpectingDeleter{1337});
+ *ptr = 1337;
+}
+
+TEST(Expected, EmptyConstruct) {
+ Expected<int, int> ex{unexpected, 42};
+ EXPECT_FALSE(bool(ex));
+ Expected<int, int> test1(ex);
+ EXPECT_FALSE(bool(test1));
+ Expected<int, int> test2(std::move(ex));
+ EXPECT_FALSE(bool(test2));
+ EXPECT_EQ(42, test2.error());
+}
+
+TEST(Expected, Unique) {
+ Expected<unique_ptr<int>, int> ex;
+
+ ex = makeUnexpected(-1);
+ EXPECT_FALSE(bool(ex));
+ // empty->emplaced
+ ex.emplace(new int(5));
+ EXPECT_TRUE(bool(ex));
+ EXPECT_EQ(5, **ex);
+
+ ex = makeUnexpected(-1);
+ // empty->moved
+ ex = unique_ptr<int>(new int(6));
+ EXPECT_EQ(6, **ex);
+ // full->moved
+ ex = unique_ptr<int>(new int(7));
+ EXPECT_EQ(7, **ex);
+
+ // move it out by move construct
+ Expected<unique_ptr<int>, int> moved(std::move(ex));
+ EXPECT_TRUE(bool(moved));
+ EXPECT_TRUE(bool(ex));
+ EXPECT_EQ(nullptr, ex->get());
+ EXPECT_EQ(7, **moved);
+
+ EXPECT_TRUE(bool(moved));
+ ex = std::move(moved); // move it back by move assign
+ EXPECT_TRUE(bool(moved));
+ EXPECT_EQ(nullptr, moved->get());
+ EXPECT_TRUE(bool(ex));
+ EXPECT_EQ(7, **ex);
+}
+
+TEST(Expected, Shared) {
+ shared_ptr<int> ptr;
+ Expected<shared_ptr<int>, int> ex{unexpected, -1};
+ EXPECT_FALSE(bool(ex));
+ // empty->emplaced
+ ex.emplace(new int(5));
+ EXPECT_TRUE(bool(ex));
+ ptr = ex.value();
+ EXPECT_EQ(ptr.get(), ex->get());
+ EXPECT_EQ(2, ptr.use_count());
+ ex = makeUnexpected(-1);
+ EXPECT_EQ(1, ptr.use_count());
+ // full->copied
+ ex = ptr;
+ EXPECT_EQ(2, ptr.use_count());
+ EXPECT_EQ(ptr.get(), ex->get());
+ ex = makeUnexpected(-1);
+ EXPECT_EQ(1, ptr.use_count());
+ // full->moved
+ ex = std::move(ptr);
+ EXPECT_EQ(1, ex->use_count());
+ EXPECT_EQ(nullptr, ptr.get());
+ {
+ EXPECT_EQ(1, ex->use_count());
+ Expected<shared_ptr<int>, int> copied(ex);
+ EXPECT_EQ(2, ex->use_count());
+ Expected<shared_ptr<int>, int> moved(std::move(ex));
+ EXPECT_EQ(2, moved->use_count());
+ moved.emplace(new int(6));
+ EXPECT_EQ(1, moved->use_count());
+ copied = moved;
+ EXPECT_EQ(2, moved->use_count());
+ }
+}
+
+TEST(Expected, Order) {
+ std::vector<Expected<int, E>> vect{
+ {unexpected, E::E1}, {3}, {1}, {unexpected, E::E1}, {2},
+ };
+ std::vector<Expected<int, E>> expected{
+ {unexpected, E::E1}, {unexpected, E::E1}, {1}, {2}, {3},
+ };
+ std::sort(vect.begin(), vect.end());
+ EXPECT_EQ(vect, expected);
+}
+
+TEST(Expected, Swap) {
+ Expected<std::string, E> a;
+ Expected<std::string, E> b;
+
+ swap(a, b);
+ EXPECT_FALSE(a.hasValue());
+ EXPECT_FALSE(b.hasValue());
+
+ a = "hello";
+ EXPECT_TRUE(a.hasValue());
+ EXPECT_FALSE(b.hasValue());
+ EXPECT_EQ("hello", a.value());
+
+ swap(a, b);
+ EXPECT_FALSE(a.hasValue());
+ EXPECT_TRUE(b.hasValue());
+ EXPECT_EQ("hello", b.value());
+
+ a = "bye";
+ EXPECT_TRUE(a.hasValue());
+ EXPECT_EQ("bye", a.value());
+
+ swap(a, b);
+}
+
+TEST(Expected, Comparisons) {
+ Expected<int, E> o_;
+ Expected<int, E> o1(1);
+ Expected<int, E> o2(2);
+
+ EXPECT_TRUE(o_ <= (o_));
+ EXPECT_TRUE(o_ == (o_));
+ EXPECT_TRUE(o_ >= (o_));
+
+ EXPECT_TRUE(o1 < o2);
+ EXPECT_TRUE(o1 <= o2);
+ EXPECT_TRUE(o1 <= (o1));
+ EXPECT_TRUE(o1 == (o1));
+ EXPECT_TRUE(o1 != o2);
+ EXPECT_TRUE(o1 >= (o1));
+ EXPECT_TRUE(o2 >= o1);
+ EXPECT_TRUE(o2 > o1);
+
+ EXPECT_FALSE(o2 < o1);
+ EXPECT_FALSE(o2 <= o1);
+ EXPECT_FALSE(o2 <= o1);
+ EXPECT_FALSE(o2 == o1);
+ EXPECT_FALSE(o1 != (o1));
+ EXPECT_FALSE(o1 >= o2);
+ EXPECT_FALSE(o1 >= o2);
+ EXPECT_FALSE(o1 > o2);
+
+ /* folly::Expected explicitly doesn't support comparisons with contained value
+ EXPECT_TRUE(1 < o2);
+ EXPECT_TRUE(1 <= o2);
+ EXPECT_TRUE(1 <= o1);
+ EXPECT_TRUE(1 == o1);
+ EXPECT_TRUE(2 != o1);
+ EXPECT_TRUE(1 >= o1);
+ EXPECT_TRUE(2 >= o1);
+ EXPECT_TRUE(2 > o1);
+
+ EXPECT_FALSE(o2 < 1);
+ EXPECT_FALSE(o2 <= 1);
+ EXPECT_FALSE(o2 <= 1);
+ EXPECT_FALSE(o2 == 1);
+ EXPECT_FALSE(o2 != 2);
+ EXPECT_FALSE(o1 >= 2);
+ EXPECT_FALSE(o1 >= 2);
+ EXPECT_FALSE(o1 > 2);
+ */
+}
+
+TEST(Expected, Conversions) {
+ Expected<bool, E> mbool;
+ Expected<short, E> mshort;
+ Expected<char*, E> mstr;
+ Expected<int, E> mint;
+
+ EXPECT_FALSE((std::is_convertible<Expected<bool, E>&, bool>::value));
+ EXPECT_FALSE((std::is_convertible<Expected<short, E>&, short>::value));
+ EXPECT_FALSE((std::is_convertible<Expected<char*, E>&, char*>::value));
+ EXPECT_FALSE((std::is_convertible<Expected<int, E>&, int>::value));
+
+ // intended explicit operator bool, for if (ex).
+ bool b(mbool);
+ EXPECT_FALSE(b);
+
+ // Truthy tests work and are not ambiguous
+ if (mbool && mshort && mstr && mint) { // only checks not-empty
+ if (*mbool && *mshort && *mstr && *mint) { // only checks value
+ ;
+ }
+ }
+
+ mbool = false;
+ EXPECT_TRUE(bool(mbool));
+ EXPECT_FALSE(*mbool);
+
+ mbool = true;
+ EXPECT_TRUE(bool(mbool));
+ EXPECT_TRUE(*mbool);
+
+ mbool = {unexpected, E::E1};
+ EXPECT_FALSE(bool(mbool));
+
+ // No conversion allowed; does not compile
+ // mbool == false;
+}
+
+TEST(Expected, Pointee) {
+ Expected<int, E> x;
+ EXPECT_FALSE(get_pointer(x));
+ x = 1;
+ EXPECT_TRUE(get_pointer(x));
+ *get_pointer(x) = 2;
+ EXPECT_TRUE(*x == 2);
+ x = {unexpected, E::E1};
+ EXPECT_FALSE(get_pointer(x));
+}
+
+TEST(Expected, MakeOptional) {
+ // const L-value version
+ const std::string s("abc");
+ auto exStr = makeExpected<E>(s);
+ ASSERT_TRUE(exStr.hasValue());
+ EXPECT_EQ(*exStr, "abc");
+ *exStr = "cde";
+ EXPECT_EQ(s, "abc");
+ EXPECT_EQ(*exStr, "cde");
+
+ // L-value version
+ std::string s2("abc");
+ auto exStr2 = makeExpected<E>(s2);
+ ASSERT_TRUE(exStr2.hasValue());
+ EXPECT_EQ(*exStr2, "abc");
+ *exStr2 = "cde";
+ // it's vital to check that s2 wasn't clobbered
+ EXPECT_EQ(s2, "abc");
+
+ // L-value reference version
+ std::string& s3(s2);
+ auto exStr3 = makeExpected<E>(s3);
+ ASSERT_TRUE(exStr3.hasValue());
+ EXPECT_EQ(*exStr3, "abc");
+ *exStr3 = "cde";
+ EXPECT_EQ(s3, "abc");
+
+ // R-value ref version
+ unique_ptr<int> pInt(new int(3));
+ auto exIntPtr = makeExpected<E>(std::move(pInt));
+ EXPECT_TRUE(pInt.get() == nullptr);
+ ASSERT_TRUE(exIntPtr.hasValue());
+ EXPECT_EQ(**exIntPtr, 3);
+}
+
+#if __CLANG_PREREQ(3, 6)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-move"
+#endif
+
+TEST(Expected, SelfAssignment) {
+ Expected<std::string, E> a = "42";
+ a = a;
+ ASSERT_TRUE(a.hasValue() && a.value() == "42");
+
+ Expected<std::string, E> b = "23333333";
+ b = std::move(b);
+ ASSERT_TRUE(b.hasValue() && b.value() == "23333333");
+}
+
+#if __CLANG_PREREQ(3, 6)
+#pragma clang diagnostic pop
+#endif
+
+class ContainsExpected {
+ public:
+ ContainsExpected() {}
+ explicit ContainsExpected(int x) : ex_(x) {}
+ bool hasValue() const {
+ return ex_.hasValue();
+ }
+ int value() const {
+ return ex_.value();
+ }
+
+ ContainsExpected(const ContainsExpected& other) = default;
+ ContainsExpected& operator=(const ContainsExpected& other) = default;
+ ContainsExpected(ContainsExpected&& other) = default;
+ ContainsExpected& operator=(ContainsExpected&& other) = default;
+
+ private:
+ Expected<int, E> ex_;
+};
+
+/**
+ * Test that a class containing an Expected can be copy and move assigned.
+ * This was broken under gcc 4.7 until assignment operators were explicitly
+ * defined.
+ */
+TEST(Expected, AssignmentContained) {
+ {
+ ContainsExpected source(5), target;
+ target = source;
+ EXPECT_TRUE(target.hasValue());
+ EXPECT_EQ(5, target.value());
+ }
+
+ {
+ ContainsExpected source(5), target;
+ target = std::move(source);
+ EXPECT_TRUE(target.hasValue());
+ EXPECT_EQ(5, target.value());
+ EXPECT_TRUE(source.hasValue());
+ }
+
+ {
+ ContainsExpected ex_uninit, target(10);
+ target = ex_uninit;
+ EXPECT_FALSE(target.hasValue());
+ }
+}
+
+TEST(Expected, Exceptions) {
+ Expected<int, E> empty;
+ EXPECT_THROW(empty.value(), Unexpected<E>::BadExpectedAccess);
+}
+
+struct ThrowingBadness {
+ ThrowingBadness() noexcept(false);
+ ThrowingBadness(const ThrowingBadness&) noexcept(false);
+ ThrowingBadness(ThrowingBadness&&) noexcept(false);
+ ThrowingBadness& operator=(const ThrowingBadness&) noexcept(false);
+ ThrowingBadness& operator=(ThrowingBadness&&) noexcept(false);
+};
+
+TEST(Expected, NoThrowDefaultConstructible) {
+ EXPECT_TRUE(
+ (std::is_nothrow_default_constructible<Expected<bool, E>>::value));
+ EXPECT_TRUE(
+ (std::is_nothrow_default_constructible<Expected<std::string, E>>::value));
+ EXPECT_TRUE((std::is_nothrow_default_constructible<
+ Expected<ThrowingBadness, E>>::value));
+ EXPECT_FALSE((std::is_nothrow_default_constructible<
+ Expected<int, ThrowingBadness>>::value));
+}
+
+TEST(Expected, NoThrowMoveConstructible) {
+ EXPECT_TRUE((std::is_nothrow_move_constructible<Expected<bool, E>>::value));
+ EXPECT_TRUE((std::is_nothrow_move_constructible<
+ Expected<std::unique_ptr<int>, E>>::value));
+ EXPECT_FALSE((
+ std::is_nothrow_move_constructible<Expected<ThrowingBadness, E>>::value));
+}
+
+TEST(Expected, NoThrowMoveAssignable) {
+ EXPECT_TRUE((std::is_nothrow_move_assignable<Expected<bool, E>>::value));
+ EXPECT_TRUE((std::is_nothrow_move_assignable<
+ Expected<std::unique_ptr<int>, E>>::value));
+ EXPECT_FALSE(
+ (std::is_nothrow_move_assignable<Expected<ThrowingBadness, E>>::value));
+}
+
+struct NoDestructor {};
+
+struct WithDestructor {
+ ~WithDestructor();
+};
+
+TEST(Expected, TriviallyDestructible) {
+ // These could all be static_asserts but EXPECT_* give much nicer output on
+ // failure.
+ EXPECT_TRUE(
+ (std::is_trivially_destructible<Expected<NoDestructor, E>>::value));
+ EXPECT_TRUE((std::is_trivially_destructible<Expected<int, E>>::value));
+ EXPECT_FALSE(
+ (std::is_trivially_destructible<Expected<WithDestructor, E>>::value));
+}
+
+struct NoConstructor {};
+
+struct WithConstructor {
+ WithConstructor();
+};
+
+TEST(Expected, TriviallyCopyable) {
+ // These could all be static_asserts but EXPECT_* give much nicer output on
+ // failure.
+ EXPECT_TRUE((IsTriviallyCopyable<Expected<int, E>>::value));
+ EXPECT_TRUE((IsTriviallyCopyable<Expected<char*, E>>::value));
+ EXPECT_TRUE(
+ (IsTriviallyCopyable<Expected<NoDestructor, E>>::value));
+ EXPECT_FALSE(
+ (IsTriviallyCopyable<Expected<WithDestructor, E>>::value));
+ EXPECT_TRUE(
+ (IsTriviallyCopyable<Expected<NoConstructor, E>>::value));
+ EXPECT_FALSE(
+ (IsTriviallyCopyable<Expected<std::string, E>>::value));
+ EXPECT_FALSE(
+ (IsTriviallyCopyable<Expected<int, std::string>>::value));
+// libstdc++ with GCC 4.x doesn't have std::is_trivially_copyable
+#if (defined(__clang__) && !defined(_LIBCPP_VERSION)) || \
+ !(defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5)
+ EXPECT_TRUE(
+ (IsTriviallyCopyable<Expected<WithConstructor, E>>::value));
+#endif
+ EXPECT_TRUE(
+ (IsTriviallyCopyable<Expected<Expected<int, E>, E>>::value));
+}
+
+TEST(Expected, Then) {
+ // Lifting
+ {
+ Expected<int, E> ex = Expected<std::unique_ptr<int>, E>{
+ in_place, new int(42)}.then([](std::unique_ptr<int> p) { return *p; });
+ EXPECT_TRUE(bool(ex));
+ EXPECT_EQ(42, *ex);
+ }
+
+ // Flattening
+ {
+ Expected<int, E> ex =
+ Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+ [](std::unique_ptr<int> p) { return makeExpected<E>(*p); });
+ EXPECT_TRUE(bool(ex));
+ EXPECT_EQ(42, *ex);
+ }
+
+ // Void
+ {
+ Expected<Unit, E> ex = Expected<std::unique_ptr<int>, E>{
+ in_place, new int(42)}.then([](std::unique_ptr<int>) {});
+ EXPECT_TRUE(bool(ex));
+ }
+
+ // Non-flattening (different error codes)
+ {
+ Expected<Expected<int, int>, E> ex =
+ Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+ [](std::unique_ptr<int> p) { return makeExpected<int>(*p); });
+ EXPECT_TRUE(bool(ex));
+ EXPECT_TRUE(bool(*ex));
+ EXPECT_EQ(42, **ex);
+ }
+
+ {
+ // Error case:
+ Expected<int, E> ex = Expected<std::unique_ptr<int>, E>{
+ unexpected, E::E1}.then([](std::unique_ptr<int> p) -> int {
+ EXPECT_TRUE(false);
+ return *p;
+ });
+ EXPECT_FALSE(bool(ex));
+ EXPECT_EQ(E::E1, ex.error());
+ }
+
+ // Chaining
+ {
+ Expected<std::string, E> ex =
+ Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+ [](std::unique_ptr<int> p) { return makeExpected<E>(*p); },
+ [](int i) { return i == 42 ? "yes" : "no"; });
+ EXPECT_TRUE(bool(ex));
+ EXPECT_EQ("yes", *ex);
+ }
+
+ // Chaining with errors
+ {
+ Expected<std::string, E> ex =
+ Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+ [](std::unique_ptr<int>) {
+ return Expected<int, E>(unexpected, E::E1);
+ },
+ [](int i) { return i == 42 ? "yes" : "no"; });
+ EXPECT_FALSE(bool(ex));
+ EXPECT_EQ(E::E1, ex.error());
+ }
+}
+
+TEST(Expected, ThenOrThrow) {
+ {
+ int e =
+ Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.thenOrThrow(
+ [](std::unique_ptr<int> p) { return *p; });
+ EXPECT_EQ(42, e);
+ }
+
+ {
+ EXPECT_THROW(
+ (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+ [](std::unique_ptr<int> p) { return *p; })),
+ Unexpected<E>::BadExpectedAccess);
+ }
+
+ {
+ EXPECT_THROW(
+ (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+ [](std::unique_ptr<int> p) { return *p; },
+ [](E) { return std::runtime_error(""); })),
+ std::runtime_error);
+ }
+
+ {
+ EXPECT_THROW(
+ (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+ [](std::unique_ptr<int> p) { return *p; },
+ [](E) { throw std::runtime_error(""); })),
+ std::runtime_error);
+ }
+
+ {
+ EXPECT_THROW(
+ (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+ [](std::unique_ptr<int> p) { return *p; }, [](E) {})),
+ Unexpected<E>::BadExpectedAccess);
+ }
+}
+}