X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FOptional.h;h=09b831f8ae2804317d6a0a74559e1ad6824bb1ff;hb=8cb615a27594078056b2e4ec2350660e594f5a89;hp=b168b59a768f825eb77c7e0334dc5fafdb99abea;hpb=8c89c7ddb2690e391521420253724a4ab1fb237b;p=folly.git diff --git a/folly/Optional.h b/folly/Optional.h index b168b59a..09b831f8 100644 --- a/folly/Optional.h +++ b/folly/Optional.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 Facebook, Inc. + * Copyright 2016 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,12 +53,11 @@ * cout << *v << endl; * } */ -#include -#include #include +#include +#include #include - -#include +#include namespace folly { @@ -73,69 +71,74 @@ 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. */ -#ifdef __GNUC__ +#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 : boost::totally_ordered, - boost::totally_ordered, Value>> { - typedef void (Optional::*bool_type)() const; - void truthy() const {}; +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) { + Optional(const Optional& src) + noexcept(std::is_nothrow_copy_constructible::value) { + if (src.hasValue()) { construct(src.value()); - } else { - hasValue_ = false; } } - Optional(Optional&& src) { + Optional(Optional&& src) + noexcept(std::is_nothrow_move_constructible::value) { + if (src.hasValue()) { construct(std::move(src.value())); src.clear(); - } else { - hasValue_ = false; } } - /* implicit */ Optional(const None& empty) - : hasValue_(false) { + /* implicit */ Optional(const None&) noexcept { } - /* implicit */ Optional(Value&& newValue) { + /* implicit */ Optional(Value&& newValue) + noexcept(std::is_nothrow_move_constructible::value) { construct(std::move(newValue)); } - /* implicit */ Optional(const Value& newValue) { + /* implicit */ Optional(const Value& newValue) + noexcept(std::is_nothrow_copy_constructible::value) { construct(newValue); } - ~Optional() { - clear(); - } - void assign(const None&) { clear(); } void assign(Optional&& src) { - if (src.hasValue()) { - assign(std::move(src.value())); - src.clear(); - } else { - clear(); + if (this != &src) { + if (src.hasValue()) { + assign(std::move(src.value())); + src.clear(); + } else { + clear(); + } } } @@ -149,7 +152,7 @@ class Optional : boost::totally_ordered, void assign(Value&& newValue) { if (hasValue()) { - value_ = std::move(newValue); + storage_.value = std::move(newValue); } else { construct(std::move(newValue)); } @@ -157,7 +160,7 @@ class Optional : boost::totally_ordered, void assign(const Value& newValue) { if (hasValue()) { - value_ = newValue; + storage_.value = newValue; } else { construct(newValue); } @@ -169,30 +172,18 @@ class Optional : boost::totally_ordered, return *this; } - bool operator<(const Optional& other) const { - if (hasValue() != other.hasValue()) { - return hasValue() < other.hasValue(); - } - if (hasValue()) { - return value() < other.value(); - } - return false; // both empty - } + Optional& operator=(Optional &&other) + noexcept (std::is_nothrow_move_assignable::value) { - bool operator<(const Value& other) const { - return !hasValue() || value() < other; + assign(std::move(other)); + return *this; } - bool operator==(const Optional& other) const { - if (hasValue()) { - return other.hasValue() && value() == other.value(); - } else { - return !other.hasValue(); - } - } + Optional& operator=(const Optional &other) + noexcept (std::is_nothrow_copy_assignable::value) { - bool operator==(const Value& other) const { - return hasValue() && value() == other; + assign(other); + return *this; } template @@ -202,59 +193,130 @@ class Optional : boost::totally_ordered, } void clear() { - if (hasValue()) { - hasValue_ = false; - value_.~Value(); - } + storage_.clear(); + } + + const Value& value() const& { + require_value(); + return storage_.value; } - const Value& value() const { - assert(hasValue()); - return value_; + Value& value() & { + require_value(); + return storage_.value; } - Value& value() { - assert(hasValue()); - return value_; + Value value() && { + require_value(); + return std::move(storage_.value); } - bool hasValue() const { return hasValue_; } + const Value* get_pointer() const& { + return storage_.hasValue ? &storage_.value : nullptr; + } + Value* get_pointer() & { + return storage_.hasValue ? &storage_.value : nullptr; + } + Value* get_pointer() && = delete; - /* safe bool idiom */ - operator bool_type() const { - return hasValue() ? &Optional::truthy : nullptr; + 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(); } + Value operator*() && { return std::move(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; + } + + return std::forward(dflt); + } + + template + Value value_or(U&& dflt) && { + if (storage_.hasValue) { + return std::move(storage_.value); + } + + return std::forward(dflt); + } + private: + void require_value() const { + if (!storage_.hasValue) { + throw OptionalEmptyException(); + } + } + 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 { + // uninitialized + union { Value value; }; + bool hasValue; + + StorageTriviallyDestructible() : hasValue{false} {} + + void clear() { + hasValue = false; + } + }; + + struct StorageNonTriviallyDestructible { + // uninitialized + union { Value value; }; + bool hasValue; + + StorageNonTriviallyDestructible() : hasValue{false} {} + + ~StorageNonTriviallyDestructible() { + clear(); + } + + void clear() { + if (hasValue) { + hasValue = false; + value.~Value(); + } + } + }; + + using Storage = + typename std::conditional::value, + StorageTriviallyDestructible, + StorageNonTriviallyDestructible>::type; + + Storage storage_; }; +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop +#endif 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 @@ -274,6 +336,73 @@ Opt make_optional(T&& v) { return Opt(std::forward(v)); } -} // namespace folly +/////////////////////////////////////////////////////////////////////////////// +// Comparisons. -#endif//FOLLY_OPTIONAL_H_ +template +bool operator==(const Optional& a, const V& b) { + return a.hasValue() && a.value() == b; +} + +template +bool operator!=(const Optional& a, const V& b) { + return !(a == b); +} + +template +bool operator==(const V& a, const Optional& b) { + return b.hasValue() && b.value() == a; +} + +template +bool operator!=(const V& 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(); } + return true; +} + +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(); } + return false; +} + +template +bool operator> (const Optional& a, const Optional& b) { + return b < a; +} + +template +bool operator<=(const Optional& a, const Optional& b) { + return !(b < a); +} + +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; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace folly