X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FOptional.h;h=f8d39cb8cc39e0ce9b4407041e5a9d329be7e825;hb=ea815395588bf55ce57f505e05f5df469edf3436;hp=5350b2ee5e44d69c55868c0a96e2e24d286a16fb;hpb=c141f6ec365fcf87af33f7de9ad66d62520729c1;p=folly.git diff --git a/folly/Optional.h b/folly/Optional.h index 5350b2ee..f8d39cb8 100644 --- a/folly/Optional.h +++ b/folly/Optional.h @@ -53,6 +53,7 @@ * cout << *v << endl; * } */ + #include #include #include @@ -60,11 +61,20 @@ #include #include +#include #include +#include namespace folly { -namespace detail { struct NoneHelper {}; } +namespace detail { +struct NoneHelper {}; + +// Allow each translation unit to control its own -fexceptions setting. +// If exceptions are disabled, std::terminate() will be called instead of +// throwing OptionalEmptyException when the condition fails. +[[noreturn]] void throw_optional_empty_exception(); +} typedef int detail::NoneHelper::*None; @@ -76,47 +86,51 @@ class OptionalEmptyException : public std::runtime_error { : std::runtime_error("Empty Optional cannot be unwrapped") {} }; -template +template class Optional { public: typedef Value value_type; - static_assert(!std::is_reference::value, - "Optional may not be used with reference types"); - static_assert(!std::is_abstract::value, - "Optional may not be used with abstract types"); - - Optional() noexcept { - } + static_assert( + !std::is_reference::value, + "Optional may not be used with reference types"); + static_assert( + !std::is_abstract::value, + "Optional may not be used with abstract types"); - Optional(const Optional& src) - noexcept(std::is_nothrow_copy_constructible::value) { + Optional() noexcept {} + Optional(const Optional& src) noexcept( + std::is_nothrow_copy_constructible::value) { if (src.hasValue()) { - construct(src.value()); + storage_.construct(src.value()); } } - Optional(Optional&& src) - noexcept(std::is_nothrow_move_constructible::value) { - + Optional(Optional&& src) noexcept( + std::is_nothrow_move_constructible::value) { if (src.hasValue()) { - construct(std::move(src.value())); + storage_.construct(std::move(src.value())); src.clear(); } } - /* implicit */ Optional(const None&) noexcept { + /* implicit */ Optional(const None&) noexcept {} + + /* implicit */ Optional(Value&& newValue) noexcept( + std::is_nothrow_move_constructible::value) { + storage_.construct(std::move(newValue)); } - /* implicit */ Optional(Value&& newValue) - noexcept(std::is_nothrow_move_constructible::value) { - construct(std::move(newValue)); + /* implicit */ Optional(const Value& newValue) noexcept( + std::is_nothrow_copy_constructible::value) { + storage_.construct(newValue); } - /* implicit */ Optional(const Value& newValue) - noexcept(std::is_nothrow_copy_constructible::value) { - construct(newValue); + template + explicit Optional(in_place_t, Args&&... args) noexcept( + std::is_nothrow_constructible::value) { + storage_.construct(std::forward(args)...); } void assign(const None&) { @@ -144,97 +158,109 @@ class Optional { void assign(Value&& newValue) { if (hasValue()) { - storage_.value = std::move(newValue); + *storage_.value_pointer() = std::move(newValue); } else { - construct(std::move(newValue)); + storage_.construct(std::move(newValue)); } } void assign(const Value& newValue) { if (hasValue()) { - storage_.value = newValue; + *storage_.value_pointer() = newValue; } else { - construct(newValue); + storage_.construct(newValue); } } - template + template Optional& operator=(Arg&& arg) { assign(std::forward(arg)); return *this; } - Optional& operator=(Optional &&other) - noexcept (std::is_nothrow_move_assignable::value) { - + Optional& operator=(Optional&& other) noexcept( + std::is_nothrow_move_assignable::value) { assign(std::move(other)); return *this; } - Optional& operator=(const Optional &other) - noexcept (std::is_nothrow_copy_assignable::value) { - + Optional& operator=(const Optional& other) noexcept( + std::is_nothrow_copy_assignable::value) { assign(other); return *this; } - template + template void emplace(Args&&... args) { clear(); - construct(std::forward(args)...); + storage_.construct(std::forward(args)...); } void clear() { storage_.clear(); } - const Value& value() const& { + const Value& value() const & { require_value(); - return storage_.value; + return *storage_.value_pointer(); } Value& value() & { require_value(); - return storage_.value; + return *storage_.value_pointer(); } Value&& value() && { require_value(); - return std::move(storage_.value); + return std::move(*storage_.value_pointer()); } - const Value&& value() const&& { + const Value&& value() const && { require_value(); - return std::move(storage_.value); + return std::move(*storage_.value_pointer()); } - const Value* get_pointer() const& { - return storage_.hasValue ? &storage_.value : nullptr; + const Value* get_pointer() const & { + return storage_.value_pointer(); } Value* get_pointer() & { - return storage_.hasValue ? &storage_.value : nullptr; + return storage_.value_pointer(); } Value* get_pointer() && = delete; - bool hasValue() const { return storage_.hasValue; } + bool hasValue() const { + return storage_.hasValue(); + } explicit operator bool() const { return hasValue(); } - const Value& operator*() const& { return value(); } - Value& operator*() & { return value(); } - const Value&& operator*() const&& { return std::move(value()); } - Value&& operator*() && { return std::move(value()); } + const Value& operator*() const & { + return value(); + } + Value& operator*() & { + return value(); + } + const Value&& operator*() const && { + return std::move(value()); + } + Value&& operator*() && { + return std::move(value()); + } - const Value* operator->() const { return &value(); } - Value* operator->() { return &value(); } + const Value* operator->() const { + return &value(); + } + 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& { - if (storage_.hasValue) { - return storage_.value; + Value value_or(U&& dflt) const & { + if (storage_.hasValue()) { + return *storage_.value_pointer(); } return std::forward(dflt); @@ -242,8 +268,8 @@ class Optional { template Value value_or(U&& dflt) && { - if (storage_.hasValue) { - return std::move(storage_.value); + if (storage_.hasValue()) { + return std::move(*storage_.value_pointer()); } return std::forward(dflt); @@ -251,88 +277,92 @@ class Optional { private: void require_value() const { - if (!storage_.hasValue) { - throw OptionalEmptyException(); + if (!storage_.hasValue()) { + detail::throw_optional_empty_exception(); } } - template - void construct(Args&&... args) { - const void* ptr = &storage_.value; - // for supporting const types - new(const_cast(ptr)) Value(std::forward(args)...); - storage_.hasValue = true; - } - struct StorageTriviallyDestructible { - // The union trick allows to initialize the Optional's memory, - // so that compiler/tools don't complain about uninitialized memory, - // without actually calling Value's default constructor. - // The rest of the implementation enforces that hasValue/value are - // synchronized. - union { - bool hasValue; - struct { - bool paddingForHasValue_[1]; - Value value; - }; - }; - - StorageTriviallyDestructible() : hasValue{false} {} + protected: + bool hasValue_; + typename std::aligned_storage::type + value_[1]; + public: + StorageTriviallyDestructible() : hasValue_{false} {} void clear() { - hasValue = false; + hasValue_ = false; } }; struct StorageNonTriviallyDestructible { - // See StorageTriviallyDestructible's union - union { - bool hasValue; - struct { - bool paddingForHasValue_[1]; - Value value; - }; - }; - - FOLLY_PUSH_WARNING - // These are both informational warnings, but they trigger rare enough - // that we've left them enabled. - FOLLY_MSVC_DISABLE_WARNING(4587) // constructor of .value is not called - FOLLY_MSVC_DISABLE_WARNING(4588) // destructor of .value is not called - StorageNonTriviallyDestructible() : hasValue{false} {} + protected: + bool hasValue_; + typename std::aligned_storage::type + value_[1]; + + public: + StorageNonTriviallyDestructible() : hasValue_{false} {} ~StorageNonTriviallyDestructible() { clear(); } - FOLLY_POP_WARNING void clear() { - if (hasValue) { - hasValue = false; - value.~Value(); + if (hasValue_) { + hasValue_ = false; + launder(reinterpret_cast(value_))->~Value(); } } }; - using Storage = - typename std::conditional::value, - StorageTriviallyDestructible, - StorageNonTriviallyDestructible>::type; + struct Storage : std::conditional< + std::is_trivially_destructible::value, + StorageTriviallyDestructible, + StorageNonTriviallyDestructible>::type { + bool hasValue() const { + return this->hasValue_; + } + + Value* value_pointer() { + if (this->hasValue_) { + return launder(reinterpret_cast(this->value_)); + } + return nullptr; + } + + Value const* value_pointer() const { + if (this->hasValue_) { + return launder(reinterpret_cast(this->value_)); + } + return nullptr; + } + + template + void construct(Args&&... args) { + new (raw_pointer()) Value(std::forward(args)...); + this->hasValue_ = true; + } + + private: + void* raw_pointer() { + return static_cast(this->value_); + } + }; Storage storage_; }; -template +template const T* get_pointer(const Optional& opt) { return opt.get_pointer(); } -template +template T* get_pointer(Optional& opt) { return opt.get_pointer(); } -template +template void swap(Optional& a, Optional& b) { if (a.hasValue() && b.hasValue()) { // both full @@ -343,8 +373,7 @@ void swap(Optional& a, Optional& b) { } } -template::type>> +template ::type>> Opt make_optional(T&& v) { return Opt(std::forward(v)); } @@ -352,69 +381,85 @@ Opt make_optional(T&& v) { /////////////////////////////////////////////////////////////////////////////// // Comparisons. -template -bool operator==(const Optional& a, const V& b) { +template +bool operator==(const Optional& a, const V& b) { return a.hasValue() && a.value() == b; } -template -bool operator!=(const Optional& a, const V& b) { +template +bool operator!=(const Optional& a, const V& b) { return !(a == b); } -template -bool operator==(const V& a, const Optional& b) { +template +bool operator==(const U& a, const Optional& b) { return b.hasValue() && b.value() == a; } -template -bool operator!=(const V& a, const Optional& b) { +template +bool operator!=(const U& a, const Optional& b) { return !(a == b); } -template -bool operator==(const Optional& a, const Optional& b) { - if (a.hasValue() != b.hasValue()) { return false; } - if (a.hasValue()) { return a.value() == b.value(); } +template +bool operator==(const Optional& a, const Optional& b) { + if (a.hasValue() != b.hasValue()) { + return false; + } + if (a.hasValue()) { + return a.value() == b.value(); + } return true; } -template -bool operator!=(const Optional& a, const Optional& b) { +template +bool operator!=(const Optional& a, const Optional& b) { return !(a == b); } -template -bool operator< (const Optional& a, const Optional& b) { - if (a.hasValue() != b.hasValue()) { return a.hasValue() < b.hasValue(); } - if (a.hasValue()) { return a.value() < b.value(); } +template +bool operator<(const Optional& a, const Optional& b) { + if (a.hasValue() != b.hasValue()) { + return a.hasValue() < b.hasValue(); + } + if (a.hasValue()) { + return a.value() < b.value(); + } return false; } -template -bool operator> (const Optional& a, const Optional& b) { +template +bool operator>(const Optional& a, const Optional& b) { return b < a; } -template -bool operator<=(const Optional& a, const Optional& b) { +template +bool operator<=(const Optional& a, const Optional& b) { return !(b < a); } -template -bool operator>=(const Optional& a, const Optional& b) { +template +bool operator>=(const Optional& a, const Optional& b) { return !(a < b); } // Suppress comparability of Optional with T, despite implicit conversion. -template bool operator< (const Optional&, const V& other) = delete; -template bool operator<=(const Optional&, const V& other) = delete; -template bool operator>=(const Optional&, const V& other) = delete; -template bool operator> (const Optional&, const V& other) = delete; -template bool operator< (const V& other, const Optional&) = delete; -template bool operator<=(const V& other, const Optional&) = delete; -template bool operator>=(const V& other, const Optional&) = delete; -template bool operator> (const V& other, const Optional&) = delete; +template +bool operator<(const Optional&, const V& other) = delete; +template +bool operator<=(const Optional&, const V& other) = delete; +template +bool operator>=(const Optional&, const V& other) = delete; +template +bool operator>(const Optional&, const V& other) = delete; +template +bool operator<(const V& other, const Optional&) = delete; +template +bool operator<=(const V& other, const Optional&) = delete; +template +bool operator>=(const V& other, const Optional&) = delete; +template +bool operator>(const V& other, const Optional&) = delete; ///////////////////////////////////////////////////////////////////////////////