12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529 |
- /*
- * 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
|