Permalink
Switch branches/tags
v2018.11.12.00 v2018.11.05.00 v2018.10.29.00 v2018.10.22.00 v2018.10.15.00 v2018.10.08.00 v2018.10.01.00 v2018.09.24.00 v2018.09.17.00 v2018.09.10.01 v2018.09.10.00 v2018.09.03.01 v2018.09.03.00 v2018.08.27.00 v2018.08.20.00 v2018.08.13.00 v2018.08.09.00 v2018.08.06.00 v2018.07.30.00 v2018.07.23.00 v2018.07.16.00 v2018.07.09.00 v2018.07.02.00 v2018.06.25.00 v2018.06.18.00 v2018.06.11.00 v2018.06.04.00 v2018.05.28.00 v2018.05.21.00 v2018.05.14.00 v2018.05.07.00 v2018.04.30.00 v2018.04.23.00 v2018.04.16.00 v2018.04.09.00 v2018.04.02.00 v2018.03.26.00 v2018.03.19.00 v2018.03.12.00 v2018.03.05.00 v2018.02.26.00 v2018.02.19.00 v2018.02.12.00 v2018.02.05.00 v2018.01.29.00 v2018.01.22.00 v2018.01.15.00 v2018.01.08.00 v2018.01.01.00 v2017.12.25.00 v2017.12.18.00 v2017.12.11.00 v2017.12.04.00 v2017.11.27.00 v2017.11.20.00 v2017.11.13.00 v2017.11.06.00 v2017.10.30.00 v2017.10.23.00 v2017.10.16.00 v2017.10.09.00 v2017.10.02.00 v2017.09.25.00 v2017.09.18.00 v2017.09.11.00 v2017.09.04.00 v2017.08.28.00 v2017.08.21.00 v2017.08.14.00 v2017.08.07.00 v2017.07.31.00 v2017.07.24.00 v2017.07.17.01 v2017.07.17.00 v2017.07.10.00 v2017.07.03.00 v2017.06.26.01 v2017.06.26.00 v2017.06.19.00 v2017.06.12.00 v2017.06.05.00 v2017.05.29.00 v2017.05.22.00 v2017.05.15.00 v2017.05.08.00 v2017.05.01.00 v2017.04.24.00 v2017.04.17.00 v2017.04.10.00 v2017.04.03.00 v2017.03.27.00 v2017.03.20.00 v2017.03.13.00 v2017.03.06.00 v2016.12.19.00 v2016.12.12.00 v2016.12.05.00 v2016.11.28.00 v2016.11.21.00 v2016.11.14.00
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1530 lines (1380 sloc) 47.7 KB
/*
* Copyright 2016-present 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* 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/ColdClass.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__)>
/**
* 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 <typename Value, typename Error>
struct PromiseReturn;
#ifdef _MSC_VER
// MSVC 2015 can't handle the StrictConjunction, so we have
// to use std::conjunction instead.
template <template <class...> class Trait, class... Ts>
using StrictAllOf = std::conjunction<Trait<Ts>...>;
#else
template <template <class...> class Trait, class... Ts>
using StrictAllOf = StrictConjunction<Trait<Ts>...>;
#endif
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;
default:
this->clear();
break;
}
}
Value& value() & {
return value_;
}
const Value& value() const& {
return value_;
}
Value&& value() && {
return std::move(value_);
}
// TODO (t17322426): remove when VS2015 support is deprecated
// VS2015 static analyzer incorrectly flags these as unreachable in certain
// circumstances. VS2017 does not have this problem on the same code.
FOLLY_PUSH_WARNING
FOLLY_MSVC_DISABLE_WARNING(4702) // unreachable code
Error& error() & {
return error_;
}
const Error& error() const& {
return error_;
}
Error&& error() && {
return std::move(error_);
}
FOLLY_POP_WARNING
};
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;
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;
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_);
}
// TODO (t17322426): remove when VS2015 support is deprecated
// VS2015 static analyzer incorrectly flags these as unreachable in certain
// circumstances. VS2017 does not have this problem on the same code.
FOLLY_PUSH_WARNING
FOLLY_MSVC_DISABLE_WARNING(4702) // unreachable code
Error& error() & {
return error_;
}
const Error& error() const& {
return error_;
}
Error&& error() && {
return std::move(error_);
}
FOLLY_POP_WARNING
};
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 : ColdClass {
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;
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_);
}
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 = 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
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
#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