X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FOptional.h;h=16cf86a10090d75575749c34fbad902f4c47d83f;hb=22d531a8fe503001a51672750dc09daae252fbf6;hp=032022b17883138083f178de17a14a4d6bf08507;hpb=3550ca21cd965645334d5b42876fa6fa2dfcb8f1;p=folly.git diff --git a/folly/Optional.h b/folly/Optional.h index 032022b1..16cf86a1 100644 --- a/folly/Optional.h +++ b/folly/Optional.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FOLLY_OPTIONAL_H_ -#define FOLLY_OPTIONAL_H_ +#pragma once /* * Optional - For conditional initialization of values, like boost::optional, @@ -54,42 +53,47 @@ * cout << *v << endl; * } */ -#include #include +#include +#include +#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; const None none = nullptr; -/** - * 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__ +class OptionalEmptyException : public std::runtime_error { + public: + OptionalEmptyException() + : std::runtime_error("Empty Optional cannot be unwrapped") {} +}; 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() - : hasValue_(false) { + Optional() noexcept { } Optional(const Optional& src) @@ -97,8 +101,6 @@ class Optional { if (src.hasValue()) { construct(src.value()); - } else { - hasValue_ = false; } } @@ -108,13 +110,10 @@ class Optional { if (src.hasValue()) { construct(std::move(src.value())); src.clear(); - } else { - hasValue_ = false; } } - /* implicit */ Optional(const None&) noexcept - : hasValue_(false) { + /* implicit */ Optional(const None&) noexcept { } /* implicit */ Optional(Value&& newValue) @@ -127,10 +126,6 @@ class Optional { construct(newValue); } - ~Optional() noexcept { - clear(); - } - void assign(const None&) { clear(); } @@ -156,7 +151,7 @@ class Optional { void assign(Value&& newValue) { if (hasValue()) { - value_ = std::move(newValue); + storage_.value = std::move(newValue); } else { construct(std::move(newValue)); } @@ -164,7 +159,7 @@ class Optional { void assign(const Value& newValue) { if (hasValue()) { - value_ = newValue; + storage_.value = newValue; } else { construct(newValue); } @@ -197,30 +192,47 @@ class Optional { } void clear() { - if (hasValue()) { - hasValue_ = false; - value_.~Value(); - } + storage_.clear(); + } + + const Value& value() const& { + require_value(); + return storage_.value; + } + + Value& value() & { + require_value(); + return storage_.value; + } + + Value&& value() && { + require_value(); + return std::move(storage_.value); } - const Value& value() const { - assert(hasValue()); - return value_; + const Value&& value() const&& { + require_value(); + return std::move(storage_.value); } - Value& value() { - assert(hasValue()); - return value_; + const Value* get_pointer() const& { + return storage_.hasValue ? &storage_.value : nullptr; } + Value* get_pointer() & { + return storage_.hasValue ? &storage_.value : nullptr; + } + Value* get_pointer() && = delete; - bool hasValue() const { return 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 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(); } @@ -228,40 +240,103 @@ class Optional { // Return a copy of the value if set, or a given default if not. template Value value_or(U&& dflt) const& { - return hasValue_ ? value_ : std::forward(dflt); + if (storage_.hasValue) { + return storage_.value; + } + + return std::forward(dflt); } template Value value_or(U&& dflt) && { - return hasValue_ ? std::move(value_) : std::forward(dflt); + if (storage_.hasValue) { + return std::move(storage_.value); + } + + return std::forward(dflt); } private: + void require_value() const { + if (!storage_.hasValue) { + detail::throw_optional_empty_exception(); + } + } + template void construct(Args&&... args) { - const void* ptr = &value_; + const void* ptr = &storage_.value; // for supporting const types new(const_cast(ptr)) Value(std::forward(args)...); - hasValue_ = true; + storage_.hasValue = true; } - // uninitialized - union { Value value_; }; - bool hasValue_; -}; + 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} {} + + void clear() { + 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} {} + ~StorageNonTriviallyDestructible() { + clear(); + } + FOLLY_POP_WARNING + + void clear() { + if (hasValue) { + hasValue = false; + value.~Value(); + } + } + }; -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif + using Storage = + typename std::conditional::value, + StorageTriviallyDestructible, + StorageNonTriviallyDestructible>::type; + + Storage storage_; +}; template const T* get_pointer(const Optional& opt) { - return opt ? &opt.value() : nullptr; + return opt.get_pointer(); } template T* get_pointer(Optional& opt) { - return opt ? &opt.value() : nullptr; + return opt.get_pointer(); } template @@ -284,61 +359,61 @@ 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) { +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) { +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); } -// To supress comparability of Optional with T, despite implicit conversion. +// 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; @@ -352,4 +427,15 @@ template bool operator> (const V& other, const Optional&) = delete; } // namespace folly -#endif // FOLLY_OPTIONAL_H_ +// Allow usage of Optional in std::unordered_map and std::unordered_set +FOLLY_NAMESPACE_STD_BEGIN +template +struct hash> { + size_t operator()(folly::Optional const& obj) const { + if (!obj.hasValue()) { + return 0; + } + return hash::type>()(*obj); + } +}; +FOLLY_NAMESPACE_STD_END