/* * Copyright (c) Facebook, Inc. and its affiliates. * * 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* an 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/CPortability.h> #include <folly/CppAttributes.h> #include <folly/Likely.h> #include <folly/Optional.h> #include <folly/Portability.h> #include <folly/Preprocessor.h> #include <folly/Traits.h> #include <folly/Unit.h> #include <folly/Utility.h> #include <folly/lang/Exception.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__)> 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 associated value_type */ template <class Expected> using ExpectedValueType = typename std::remove_reference<Expected>::type::value_type; /** * Alias for an Expected type's associated error_type */ template <class Expected> using ExpectedErrorType = typename std::remove_reference<Expected>::type::error_type; // Details... namespace expected_detail { template <typename Value, typename Error> struct PromiseReturn; 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 = static_cast<U&&>(u))) { t = static_cast<U&&>(u); } template <class T, class U> auto doEmplaceAssign(long, T& t, U&& u) -> decltype(void(T(static_cast<U&&>(u)))) { t.~T(); ::new ((void*)std::addressof(t)) T(static_cast<U&&>(u)); } template <class T, class... Us> auto doEmplaceAssign(int, T& t, Us&&... us) -> decltype(void(t = T(static_cast<Us&&>(us)...))) { t = T(static_cast<Us&&>(us)...); } template <class T, class... Us> auto doEmplaceAssign(long, T& t, Us&&... us) -> decltype(void(T(static_cast<Us&&>(us)...))) { t.~T(); ::new ((void*)std::addressof(t)) T(static_cast<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<is_trivially_copyable, 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; case Which::eEmpty: 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_ = Which::eEmpty; explicit constexpr ExpectedUnion(EmptyTag) noexcept {} 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 { constexpr CopyConstructible() = default; CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) { static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that)); } constexpr CopyConstructible(CopyConstructible&&) = default; CopyConstructible& operator=(const CopyConstructible&) = default; CopyConstructible& operator=(CopyConstructible&&) = default; }; template <class Derived, bool Noexcept> struct CopyConstructible<Derived, false, Noexcept> { constexpr CopyConstructible() = default; CopyConstructible(const CopyConstructible&) = delete; constexpr CopyConstructible(CopyConstructible&&) = default; CopyConstructible& operator=(const CopyConstructible&) = default; CopyConstructible& operator=(CopyConstructible&&) = default; }; template <class Derived, bool, bool Noexcept> struct MoveConstructible { constexpr MoveConstructible() = default; constexpr 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> { constexpr MoveConstructible() = default; constexpr MoveConstructible(const MoveConstructible&) = default; MoveConstructible(MoveConstructible&&) = delete; MoveConstructible& operator=(const MoveConstructible&) = default; MoveConstructible& operator=(MoveConstructible&&) = default; }; template <class Derived, bool, bool Noexcept> struct CopyAssignable { constexpr CopyAssignable() = default; constexpr CopyAssignable(const CopyAssignable&) = default; constexpr 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> { constexpr CopyAssignable() = default; constexpr CopyAssignable(const CopyAssignable&) = default; constexpr CopyAssignable(CopyAssignable&&) = default; CopyAssignable& operator=(const CopyAssignable&) = delete; CopyAssignable& operator=(CopyAssignable&&) = default; }; template <class Derived, bool, bool Noexcept> struct MoveAssignable { constexpr MoveAssignable() = default; constexpr MoveAssignable(const MoveAssignable&) = default; constexpr 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> { constexpr MoveAssignable() = default; constexpr MoveAssignable(const MoveAssignable&) = default; constexpr 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; case Which::eEmpty: 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 isSelfAssign(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; case Which::eEmpty: 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; case Which::eEmpty: 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); } FOLLY_PUSH_WARNING // Don't warn about not using the overloaded comma operator. FOLLY_MSVC_DISABLE_WARNING(4913) 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_exception(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()); typename Unexpected<ExpectedErrorType<This>>::MakeBadExpectedAccess bad; throw_exception(bad(static_cast<This&&>(ex).error())); } FOLLY_POP_WARNING }; } // namespace expected_detail_ExpectedHelper /* 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 FOLLY_EXPORT BadExpectedAccess : public std::logic_error { public: BadExpectedAccess() : std::logic_error("bad Expected access") {} }; namespace expected_detail { // empty } // namespace expected_detail /** * 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 FOLLY_EXPORT 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; FOLLY_COLD constexpr /* implicit */ Unexpected(const Error& err) : error_(err) {} FOLLY_COLD 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_); } private: struct MakeBadExpectedAccess { template <class E> BadExpectedAccess operator()(E&& err) const { return BadExpectedAccess(static_cast<E&&>(err)); } }; Error error_; }; template < class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)> inline bool operator==( const Unexpected<Error>& lhs, const Unexpected<Error>& rhs) { return lhs.error() == rhs.error(); } template < class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)> inline bool operator!=( const Unexpected<Error>& lhs, const Unexpected<Error>& rhs) { return !(lhs == rhs); } /** * 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; 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->assign(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; } // Used only when an Expected is used with coroutines on MSVC /* implicit */ Expected(const expected_detail::PromiseReturn<Value, Error>& p) : Expected{} { p.promise_->value_ = 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_exception<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 */ template <class Val, class Err> friend typename std::enable_if<IsEqualityComparable<Val>::value, bool>::type operator==(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs); template <class Val, class Err> friend typename std::enable_if<IsLessThanComparable<Val>::value, bool>::type operator<(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs); /* * Accessors */ constexpr bool hasValue() const noexcept { return LIKELY(expected_detail::Which::eValue == this->which_); } constexpr bool hasError() const noexcept { return UNLIKELY(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_exception<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_exception<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_exception<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_exception<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_exception<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_exception<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())) { using Err = typename Unexpected<Error>::BadExpectedAccess; throw_exception<Err>(this->error_); } throw_exception<BadExpectedAccess>(); } } void requireError() const { if (UNLIKELY(!hasError())) { throw_exception<BadExpectedAccess>(); } } expected_detail::Which which() const noexcept { return this->which_; } }; template <class Value, class Error> inline typename std::enable_if<IsEqualityComparable<Value>::value, bool>::type operator==( const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) { if (UNLIKELY(lhs.uninitializedByException())) { throw_exception<BadExpectedAccess>(); } if (UNLIKELY(lhs.which_ != rhs.which_)) { return false; } if (UNLIKELY(lhs.hasError())) { return true; // All error states are considered equal } return lhs.value_ == rhs.value_; } template < class Value, class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Value>::value)> inline bool operator!=( const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) { return !(rhs == lhs); } template <class Value, class Error> inline typename std::enable_if<IsLessThanComparable<Value>::value, bool>::type operator<( const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) { if (UNLIKELY( lhs.uninitializedByException() || rhs.uninitializedByException())) { throw_exception<BadExpectedAccess>(); } if (UNLIKELY(lhs.hasError())) { return !rhs.hasError(); } if (UNLIKELY(rhs.hasError())) { return false; } return lhs.value_ < rhs.value_; } template < class Value, class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)> inline bool operator<=( const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) { return !(rhs < lhs); } template < class Value, class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)> inline bool operator>( const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) { return rhs < lhs; } template < class Value, class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)> inline bool operator>=( const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) { return !(lhs < rhs); } /** * swap Expected values */ template <class Value, class Error> void swap(Expected<Value, Error>& 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 #undef FOLLY_REQUIRES #undef FOLLY_REQUIRES_TRAILING // Enable the use of folly::Expected with `co_await` // Inspired by https://github.com/toby-allsopp/coroutine_monad #if FOLLY_HAS_COROUTINES #include <experimental/coroutine> namespace folly { namespace expected_detail { template <typename Value, typename Error> struct Promise; template <typename Value, typename Error> struct PromiseReturn { Optional<Expected<Value, Error>> storage_; Promise<Value, Error>* promise_; /* implicit */ PromiseReturn(Promise<Value, Error>& promise) noexcept : promise_(&promise) { promise_->value_ = &storage_; } PromiseReturn(PromiseReturn&& that) noexcept : PromiseReturn{*that.promise_} {} ~PromiseReturn() {} /* implicit */ operator Expected<Value, Error>() & { return std::move(*storage_); } }; template <typename Value, typename Error> struct Promise { Optional<Expected<Value, Error>>* value_ = nullptr; Promise() = default; Promise(Promise const&) = delete; // This should work regardless of whether the compiler generates: // folly::Expected<Value, Error> retobj{ p.get_return_object(); } // MSVC // or: // auto retobj = p.get_return_object(); // clang PromiseReturn<Value, Error> get_return_object() noexcept { return *this; } std::experimental::suspend_never initial_suspend() const noexcept { return {}; } std::experimental::suspend_never final_suspend() const { return {}; } template <typename U> void return_value(U&& u) { value_->emplace(static_cast<U&&>(u)); } void unhandled_exception() { // Technically, throwing from unhandled_exception is underspecified: // https://github.com/GorNishanov/CoroutineWording/issues/17 throw; } }; template <typename Value, typename Error> struct Awaitable { Expected<Value, Error> o_; explicit Awaitable(Expected<Value, Error> o) : o_(std::move(o)) {} bool await_ready() const noexcept { return o_.hasValue(); } Value await_resume() { return std::move(o_.value()); } // Explicitly only allow suspension into a Promise template <typename U> void await_suspend(std::experimental::coroutine_handle<Promise<U, Error>> h) { *h.promise().value_ = makeUnexpected(std::move(o_.error())); // Abort the rest of the coroutine. resume() is not going to be called h.destroy(); } }; } // namespace expected_detail template <typename Value, typename Error> expected_detail::Awaitable<Value, Error> /* implicit */ operator co_await(Expected<Value, Error> o) { return expected_detail::Awaitable<Value, Error>{std::move(o)}; } } // namespace folly // This makes folly::Expected<Value> useable as a coroutine return type... namespace std { namespace experimental { template <typename Value, typename Error, typename... Args> struct coroutine_traits<folly::Expected<Value, Error>, Args...> { using promise_type = folly::expected_detail::Promise<Value, Error>; }; } // namespace experimental } // namespace std #endif // FOLLY_HAS_COROUTINES