blob: c516d7a8e50a8a0de31a1d12c1502118359a841c [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All Rights Reserved.
*
* 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.
*/
#ifndef BASE_OPTIONAL_H_
#define BASE_OPTIONAL_H_
#include <iosfwd>
#include "base/base_export.h"
#include "base/hash_tables.h"
#include "base/logging.h"
#include "base/memory/aligned_memory.h"
namespace base {
// This class is based off of std::experimental::optional:
// http://en.cppreference.com/w/cpp/experimental/optional
//
// It is a template class where instances parameterized by type T contain
// memory for an instance of type T, but it may or may not be constructed.
// If it is not constructed, it cannot be accessed, and if it is, it can
// be accessed. This allows one to check if the inner object exists or not
// before using it, and is useful for functions that may or may not return
// a value. Note that the memory for the object is stored internally, so
// no heap allocations are made over the course of construction and destruction
// of the internal object (unless the internal object allocates memory within
// its constructor).
//
// Some functionality is left out. For example, most C++11 functionality
// is not implemented, since we would like this to be friendly to non-C++11
// compilers.
//
// In the future, if C++11 functionality is needed, it can be implemented
// and surrounded by preprocessor guards to maintain compatibility with non
// C++11 compilers.
//
// The nullopt_t type is used as a signal for an empty optional. If any
// optional is assigned the value of nullopt, it will be disengaged.
// For example,
// base::optional<int> my_int_optional(5);
// EXPECT_FALSE(!my_int_optional);
// my_int_optional = base::nullopt;
// EXPECT_TRUE(!my_int_optional);
//
struct nullopt_t {
nullopt_t() {}
};
extern const nullopt_t nullopt;
// The in_place_t type is used to signal in-place construction of the internal
// object. This is used by the in place constructor of optional, which forwards
// its parameters to the internal object's constructor.
// For example,
// class Foo {
// public:
// Foo(int x, int y) { x_ = x; y_ = y; }
// int x() const { return x_; }
// int y() const { return y_; }
//
// private:
// int x_;
// int y_;
// };
//
// ...
//
// base::optional<Foo> my_foo(base::in_place, 2, 3);
// EXPECT_FALSE(!my_foo);
// EXPECT_EQ(2, my_foo->x());
// EXPECT_EQ(3, my_foo->y());
//
struct in_place_t {
in_place_t() {}
};
extern const in_place_t in_place;
template <typename T>
class BASE_EXPORT optional {
public:
// Construction via the default constructor results in an optional that is
// not engaged.
optional() { InitializeAsDisengaged(); }
optional(nullopt_t) { InitializeAsDisengaged(); }
// This non-explicit singleton constructor is provided so users can pass in a
// T wherever a optional<T> is expected.
optional(const T& value) { SetValue(value); }
optional(const optional<T>& other) {
if (other.engaged_) {
SetValue(other.value());
} else {
InitializeAsDisengaged();
}
}
// Destruct contained object upon optional's destruction.
~optional() { EnsureDisengaged(); }
// Disengages the optional, calling the destructor of the contained object
// if it is engaged.
optional<T>& operator=(nullopt_t) {
EnsureDisengaged();
return *this;
}
// Reassigns the underlying optional to value passed in on the right hand
// side. This will destruct the lhs contained object first if it exists.
template <typename U>
optional<T>& operator=(const U& other) {
if (engaged_) {
value() = other;
} else {
SetValue(other);
}
return *this;
}
// Copy assignment
optional<T>& operator=(const optional<T>& other) {
if (engaged_ && other.engaged_) {
value() = other.value();
} else if (!engaged_ && other.engaged_) {
SetValue(other.value());
} else if (engaged_ && !other.engaged_) {
EnsureDisengaged();
}
// Do nothing if lhs and rhs are both not engaged.
return *this;
}
// Overloaded conversion to bool operator for determining whether the optional
// is engaged or not. It returns true if the optional is engaged, and false
// otherwise.
#if defined(_MSC_VER) && _MSC_VER < 1800
// MSVC 2012 does not support explicit cast operators.
// http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
// For any compiler that doesn't support explicit bool operators, we instead
// use the Safe Bool Idiom: http://www.artima.com/cppsource/safebool.html
private:
// The type of SafeBoolIdiomType (pointer to data member of a private type) is
// limited in functionality so much that the only thing a user can do with it
// is test for null, or apply to operator==/operator!=. Since both operators
// == and != are already overloaded for optional, this leaves null tests,
// which we use for boolean testing.
class PrivateSafeBoolIdiomFakeMemberType;
typedef PrivateSafeBoolIdiomFakeMemberType optional::*SafeBoolIdiomType;
public:
operator const SafeBoolIdiomType() const {
// If we wish to return true, we cast engaged_ to our private type giving
// a non-null pointer to data member. Otherwise, we return NULL. The
// only thing the user can do with the return type is test for NULL.
return engaged_ ?
reinterpret_cast<const SafeBoolIdiomType>(&optional::engaged_) :
NULL;
}
#else
explicit operator bool() const { return engaged_; }
#endif
// Dereferences the internal object.
const T* operator->() const { return &(value()); }
T* operator->() { return &(value()); }
const T& operator*() const { return value(); }
T& operator*() { return value(); }
// Dereferences and returns the internal object.
const T& value() const {
DCHECK(engaged_) << "Attempted to access object in a disengaged optional.";
return *static_cast<const T*>(value_memory_.void_data());
}
T& value() {
DCHECK(engaged_) << "Attempted to access object in a disengaged optional.";
return *static_cast<T*>(value_memory_.void_data());
}
template <typename U>
T value_or(const U& value) const {
if (engaged_) {
return this->value();
} else {
return value;
}
}
// Swaps the values of two optionals.
void swap(optional<T>& other) {
if (engaged_ && other.engaged_) {
// Swap the value contents with each other.
std::swap(value(), other.value());
} else if (engaged_) {
other.SetValue(value());
EnsureDisengaged();
} else if (other.engaged_) {
SetValue(other.value());
other.EnsureDisengaged();
}
// If both the lhs and rhs are not engaged, we do nothing.
}
// include the pump.py-generated declaration and impelmentation for
// forwarding constructor and emplace.
#include "optional_internal.h"
private:
// Sets a non-engaged optional to a specified value, and marks it as engaged.
template <typename U>
void SetValue(const U& value) {
new (value_memory_.void_data()) T(value);
engaged_ = true;
#if !defined(NDEBUG)
value_ptr_ = static_cast<const T*>(value_memory_.void_data());
#endif
}
// If an optional is engaged, it destructs the wrapped value and marks the
// optional as disengaged. Does nothing to a disengaged optional.
void EnsureDisengaged() {
if (engaged_) {
static_cast<T*>(value_memory_.void_data())->~T();
engaged_ = false;
#if !defined(NDEBUG)
value_ptr_ = NULL;
#endif
}
}
// Called upon object construction to initialize the object into a disengaged
// state.
void InitializeAsDisengaged() {
engaged_ = false;
#if !defined(NDEBUG)
value_ptr_ = NULL;
#endif
}
// The actual memory reserved for the object that may or may not exist.
base::AlignedMemory<sizeof(T), ALIGNOF(T)> value_memory_;
// This boolean tracks whether or not the object is constructed yet or not.
bool engaged_;
#if !defined(NDEBUG)
// In debug builds, this member makes it easy to inspect the value contained
// in the optional via a debugger.
const T* value_ptr_;
#endif
};
// Comparison between 2 optionals
template <typename T>
inline bool operator==(const optional<T>& lhs, const optional<T>& rhs) {
if (!lhs) {
return !rhs;
}
return rhs == lhs.value();
}
template <typename T>
inline bool operator<(const optional<T>& lhs, const optional<T>& rhs) {
if (lhs && rhs) {
return lhs.value() < rhs.value();
} else {
// Handle all other cases simply in terms of whether the optionals are
// engaged or not.
return static_cast<bool>(lhs) < static_cast<bool>(rhs);
}
}
// Comparison with nullopt_t
template <typename T>
inline bool operator==(nullopt_t, const optional<T>& rhs) {
return !rhs;
}
template <typename T>
inline bool operator==(const optional<T>& lhs, nullopt_t rhs) {
return rhs == lhs;
}
template <typename T>
inline bool operator<(const optional<T>& /* lhs */, nullopt_t) {
return false;
}
template <typename T>
inline bool operator<(nullopt_t, const optional<T>& rhs) {
return static_cast<bool>(rhs);
}
// Comparison between an optional and a value
template <typename T>
inline bool operator==(const optional<T>& lhs, const T& rhs) {
return (!lhs ? false : lhs.value() == rhs);
}
template <typename T>
inline bool operator==(const T& lhs, const optional<T>& rhs) {
return rhs == lhs;
}
template <typename T>
inline bool operator<(const T& lhs, const optional<T>& rhs) {
return rhs && lhs < rhs.value();
}
template <typename T>
inline bool operator<(const optional<T>& lhs, const T& rhs) {
return !lhs || lhs.value() < rhs;
}
// This is a convenient but non-standard method, do not rely on it if you expect
// the compatibility with upcoming C++ versions.
template <typename T>
inline std::ostream& operator<<(std::ostream& stream,
const optional<T>& maybe_value) {
if (maybe_value) {
stream << *maybe_value;
} else {
stream << "nullopt";
}
return stream;
}
template <typename T>
optional<T> make_optional(const T& value) {
return optional<T>(value);
}
} // namespace base
namespace BASE_HASH_NAMESPACE {
#if defined(BASE_HASH_USE_HASH_STRUCT)
template <typename T>
struct hash<base::optional<T> > {
public:
size_t operator()(const base::optional<T>& value) const {
if (!value) {
return 0;
} else {
return value_hash_(value.value());
}
}
private:
hash<T> value_hash_;
};
#else
template <typename T>
inline size_t hash_value(const base::optional<T>& value) {
if (!value) {
return 0;
} else {
return hash_value(value.value());
}
}
#endif // defined(BASE_HASH_USE_HASH_STRUCT)
} // namespace BASE_HASH_NAMESPACE
namespace std {
template <typename T>
void swap(base::optional<T>& lhs, base::optional<T>& rhs) {
lhs.swap(rhs);
}
} // namespace std
#endif // BASE_OPTIONAL_H_