X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FOptional.h;h=60ae2c735b1152ca2c883263c483b4ca229af386;hb=17c08956e0c46684e3a3a9d3b5e9bc733a09c0ea;hp=cdf05201e163cb0dacb5b0b4e5a29f762dbf3b9c;hpb=99d6990ec46494f8cf9971d5eb53429a599baf0f;p=folly.git diff --git a/folly/Optional.h b/folly/Optional.h index cdf05201..60ae2c73 100644 --- a/folly/Optional.h +++ b/folly/Optional.h @@ -61,12 +61,16 @@ #include #include -#include #include +#include #include +#include namespace folly { +template +class Optional; + namespace detail { struct NoneHelper {}; @@ -74,7 +78,10 @@ struct NoneHelper {}; // If exceptions are disabled, std::terminate() will be called instead of // throwing OptionalEmptyException when the condition fails. [[noreturn]] void throw_optional_empty_exception(); -} + +template +struct OptionalPromiseReturn; +} // namespace detail typedef int detail::NoneHelper::*None; @@ -98,7 +105,7 @@ class Optional { !std::is_abstract::value, "Optional may not be used with abstract types"); - Optional() noexcept {} + FOLLY_CPP14_CONSTEXPR Optional() noexcept {} Optional(const Optional& src) noexcept( std::is_nothrow_copy_constructible::value) { @@ -115,24 +122,30 @@ class Optional { } } - /* implicit */ Optional(const None&) noexcept {} + FOLLY_CPP14_CONSTEXPR /* implicit */ Optional(const None&) noexcept {} - /* implicit */ Optional(Value&& newValue) noexcept( + FOLLY_CPP14_CONSTEXPR /* implicit */ Optional(Value&& newValue) noexcept( std::is_nothrow_move_constructible::value) { storage_.construct(std::move(newValue)); } - /* implicit */ Optional(const Value& newValue) noexcept( + FOLLY_CPP14_CONSTEXPR /* implicit */ Optional(const Value& newValue) noexcept( std::is_nothrow_copy_constructible::value) { storage_.construct(newValue); } template - explicit Optional(in_place_t, Args&&... args) noexcept( + FOLLY_CPP14_CONSTEXPR explicit Optional(in_place_t, Args&&... args) noexcept( std::is_nothrow_constructible::value) { storage_.construct(std::forward(args)...); } + // Used only when an Optional is used with coroutines on MSVC + /* implicit */ Optional(const detail::OptionalPromiseReturn& p) + : Optional{} { + p.promise_->value_ = this; + } + void assign(const None&) { clear(); } @@ -191,31 +204,57 @@ class Optional { } template - void emplace(Args&&... args) { + Value& emplace(Args&&... args) { clear(); - storage_.construct(std::forward(args)...); + return storage_.construct(std::forward(args)...); + } + + template + typename std::enable_if< + std::is_constructible&, Args&&...>::value, + Value&>::type + emplace(std::initializer_list ilist, Args&&... args) { + clear(); + return storage_.construct(ilist, std::forward(args)...); } - void clear() { + void reset() noexcept { storage_.clear(); } - const Value& value() const & { + void clear() noexcept { + reset(); + } + + void swap(Optional& that) noexcept(IsNothrowSwappable::value) { + if (hasValue() && that.hasValue()) { + using std::swap; + swap(value(), that.value()); + } else if (hasValue()) { + that.emplace(std::move(value())); + reset(); + } else if (that.hasValue()) { + emplace(std::move(that.value())); + that.reset(); + } + } + + FOLLY_CPP14_CONSTEXPR const Value& value() const & { require_value(); return *storage_.value_pointer(); } - Value& value() & { + FOLLY_CPP14_CONSTEXPR Value& value() & { require_value(); return *storage_.value_pointer(); } - Value&& value() && { + FOLLY_CPP14_CONSTEXPR Value&& value() && { require_value(); return std::move(*storage_.value_pointer()); } - const Value&& value() const && { + FOLLY_CPP14_CONSTEXPR const Value&& value() const && { require_value(); return std::move(*storage_.value_pointer()); } @@ -228,37 +267,41 @@ class Optional { } Value* get_pointer() && = delete; - bool hasValue() const { + FOLLY_CPP14_CONSTEXPR bool has_value() const noexcept { return storage_.hasValue(); } - explicit operator bool() const { - return hasValue(); + FOLLY_CPP14_CONSTEXPR bool hasValue() const noexcept { + return has_value(); + } + + FOLLY_CPP14_CONSTEXPR explicit operator bool() const noexcept { + return has_value(); } - const Value& operator*() const & { + FOLLY_CPP14_CONSTEXPR const Value& operator*() const & { return value(); } - Value& operator*() & { + FOLLY_CPP14_CONSTEXPR Value& operator*() & { return value(); } - const Value&& operator*() const && { + FOLLY_CPP14_CONSTEXPR const Value&& operator*() const && { return std::move(value()); } - Value&& operator*() && { + FOLLY_CPP14_CONSTEXPR Value&& operator*() && { return std::move(value()); } - const Value* operator->() const { + FOLLY_CPP14_CONSTEXPR const Value* operator->() const { return &value(); } - Value* operator->() { + FOLLY_CPP14_CONSTEXPR Value* operator->() { return &value(); } // Return a copy of the value if set, or a given default if not. template - Value value_or(U&& dflt) const & { + FOLLY_CPP14_CONSTEXPR Value value_or(U&& dflt) const & { if (storage_.hasValue()) { return *storage_.value_pointer(); } @@ -267,7 +310,7 @@ class Optional { } template - Value value_or(U&& dflt) && { + FOLLY_CPP14_CONSTEXPR Value value_or(U&& dflt) && { if (storage_.hasValue()) { return std::move(*storage_.value_pointer()); } @@ -285,7 +328,8 @@ class Optional { struct StorageTriviallyDestructible { protected: bool hasValue_; - std::aligned_storage_t value_[1]; + typename std::aligned_storage::type + value_[1]; public: StorageTriviallyDestructible() : hasValue_{false} {} @@ -297,7 +341,8 @@ class Optional { struct StorageNonTriviallyDestructible { protected: bool hasValue_; - std::aligned_storage_t value_[1]; + typename std::aligned_storage::type + value_[1]; public: StorageNonTriviallyDestructible() : hasValue_{false} {} @@ -313,11 +358,11 @@ class Optional { } }; - struct Storage : std::conditional_t< + struct Storage : std::conditional< std::is_trivially_destructible::value, StorageTriviallyDestructible, - StorageNonTriviallyDestructible> { - bool hasValue() const { + StorageNonTriviallyDestructible>::type { + bool hasValue() const noexcept { return this->hasValue_; } @@ -336,9 +381,10 @@ class Optional { } template - void construct(Args&&... args) { + Value& construct(Args&&... args) { new (raw_pointer()) Value(std::forward(args)...); this->hasValue_ = true; + return *launder(reinterpret_cast(this->value_)); } private: @@ -361,18 +407,12 @@ T* get_pointer(Optional& opt) { } template -void swap(Optional& a, Optional& b) { - if (a.hasValue() && b.hasValue()) { - // both full - using std::swap; - swap(a.value(), b.value()); - } else if (a.hasValue() || b.hasValue()) { - std::swap(a, b); // fall back to default implementation if they're mixed. - } +void swap(Optional& a, Optional& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); } -template >> -Opt make_optional(T&& v) { +template ::type>> +constexpr Opt make_optional(T&& v) { return Opt(std::forward(v)); } @@ -380,27 +420,29 @@ Opt make_optional(T&& v) { // Comparisons. template -bool operator==(const Optional& a, const V& b) { +constexpr bool operator==(const Optional& a, const V& b) { return a.hasValue() && a.value() == b; } template -bool operator!=(const Optional& a, const V& b) { +constexpr bool operator!=(const Optional& a, const V& b) { return !(a == b); } template -bool operator==(const U& a, const Optional& b) { +constexpr bool operator==(const U& a, const Optional& b) { return b.hasValue() && b.value() == a; } template -bool operator!=(const U& a, const Optional& b) { +constexpr bool operator!=(const U& a, const Optional& b) { return !(a == b); } template -bool operator==(const Optional& a, const Optional& b) { +FOLLY_CPP14_CONSTEXPR bool operator==( + const Optional& a, + const Optional& b) { if (a.hasValue() != b.hasValue()) { return false; } @@ -411,12 +453,14 @@ bool operator==(const Optional& a, const Optional& b) { } template -bool operator!=(const Optional& a, const Optional& b) { +constexpr bool operator!=(const Optional& a, const Optional& b) { return !(a == b); } template -bool operator<(const Optional& a, const Optional& b) { +FOLLY_CPP14_CONSTEXPR bool operator<( + const Optional& a, + const Optional& b) { if (a.hasValue() != b.hasValue()) { return a.hasValue() < b.hasValue(); } @@ -427,17 +471,17 @@ bool operator<(const Optional& a, const Optional& b) { } template -bool operator>(const Optional& a, const Optional& b) { +constexpr bool operator>(const Optional& a, const Optional& b) { return b < a; } template -bool operator<=(const Optional& a, const Optional& b) { +constexpr bool operator<=(const Optional& a, const Optional& b) { return !(b < a); } template -bool operator>=(const Optional& a, const Optional& b) { +constexpr bool operator>=(const Optional& a, const Optional& b) { return !(a < b); } @@ -459,6 +503,48 @@ bool operator>=(const V& other, const Optional&) = delete; template bool operator>(const V& other, const Optional&) = delete; +// Comparisons with none +template +constexpr bool operator==(const Optional& a, None) noexcept { + return !a.hasValue(); +} +template +constexpr bool operator==(None, const Optional& a) noexcept { + return !a.hasValue(); +} +template +constexpr bool operator<(const Optional&, None) noexcept { + return false; +} +template +constexpr bool operator<(None, const Optional& a) noexcept { + return a.hasValue(); +} +template +constexpr bool operator>(const Optional& a, None) noexcept { + return a.hasValue(); +} +template +constexpr bool operator>(None, const Optional&) noexcept { + return false; +} +template +constexpr bool operator<=(None, const Optional&) noexcept { + return true; +} +template +constexpr bool operator<=(const Optional& a, None) noexcept { + return !a.hasValue(); +} +template +constexpr bool operator>=(const Optional&, None) noexcept { + return true; +} +template +constexpr bool operator>=(None, const Optional& a) noexcept { + return !a.hasValue(); +} + /////////////////////////////////////////////////////////////////////////////// } // namespace folly @@ -471,7 +557,100 @@ struct hash> { if (!obj.hasValue()) { return 0; } - return hash>()(*obj); + return hash::type>()(*obj); } }; FOLLY_NAMESPACE_STD_END + +// Enable the use of folly::Optional with `co_await` +// Inspired by https://github.com/toby-allsopp/coroutine_monad +#if FOLLY_HAS_COROUTINES +#include + +namespace folly { +namespace detail { +template +struct OptionalPromise; + +template +struct OptionalPromiseReturn { + Optional storage_; + OptionalPromise* promise_; + /* implicit */ OptionalPromiseReturn(OptionalPromise& promise) noexcept + : promise_(&promise) { + promise.value_ = &storage_; + } + OptionalPromiseReturn(OptionalPromiseReturn&& that) noexcept + : OptionalPromiseReturn{*that.promise_} {} + ~OptionalPromiseReturn() {} + /* implicit */ operator Optional() & { + return std::move(storage_); + } +}; + +template +struct OptionalPromise { + Optional* value_ = nullptr; + OptionalPromise() = default; + OptionalPromise(OptionalPromise const&) = delete; + // This should work regardless of whether the compiler generates: + // folly::Optional retobj{ p.get_return_object(); } // MSVC + // or: + // auto retobj = p.get_return_object(); // clang + OptionalPromiseReturn get_return_object() noexcept { + return *this; + } + std::experimental::suspend_never initial_suspend() const noexcept { + return {}; + } + std::experimental::suspend_never final_suspend() const { + return {}; + } + template + void return_value(U&& u) { + *value_ = static_cast(u); + } + void unhandled_exception() { + // Technically, throwing from unhandled_exception is underspecified: + // https://github.com/GorNishanov/CoroutineWording/issues/17 + throw; + } +}; + +template +struct OptionalAwaitable { + Optional o_; + bool await_ready() const noexcept { + return o_.hasValue(); + } + Value await_resume() { + return std::move(o_.value()); + } + + // Explicitly only allow suspension into an OptionalPromise + template + void await_suspend( + std::experimental::coroutine_handle> h) const { + // Abort the rest of the coroutine. resume() is not going to be called + h.destroy(); + } +}; +} // namespace detail + +template +detail::OptionalAwaitable +/* implicit */ operator co_await(Optional o) { + return {std::move(o)}; +} +} // namespace folly + +// This makes folly::Optional useable as a coroutine return type.. +namespace std { +namespace experimental { +template +struct coroutine_traits, Args...> { + using promise_type = folly::detail::OptionalPromise; +}; +} // namespace experimental +} // namespace std +#endif // FOLLY_HAS_COROUTINES