blob: 4c892f50960aaa6e65ddbb571ee009b08adb1955 [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 STARBOARD_COMMON_OPTIONAL_H_
#define STARBOARD_COMMON_OPTIONAL_H_
#include <algorithm>
#include <iosfwd>
#include "starboard/configuration.h"
#include "starboard/export.h"
#include "starboard/log.h"
#include "starboard/memory.h"
namespace starboard {
// 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,
// starboard::optional<int> my_int_optional(5);
// EXPECT_FALSE(!my_int_optional);
// my_int_optional = starboard::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 SB_EXPORT optional {
public:
// Construction via the default constructor results in an optional that is
// not engaged.
optional() { InitializeAsDisengaged(); }
optional(nullopt_t) { InitializeAsDisengaged(); } // NOLINT(runtime/explicit)
// 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); } // NOLINT(runtime/explicit)
optional(const optional<T>& other) { // NOLINT(runtime/explicit)
if (other.engaged_) {
SetValue(other.value());
} else {
InitializeAsDisengaged();
}
}
// Move constructor.
// NOLINTNEXTLINE(runtime/explicit)
optional(optional&& other) { // NOLINT(build/c++11)
if (other.engaged_) {
SetValue(std::move(other.value()));
} else {
InitializeAsDisengaged();
}
}
// Move value constructor.
// NOLINTNEXTLINE(runtime/explicit)
optional(T&& other) { // NOLINT(build/c++11)
SetValue(std::move(other));
}
// 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;
}
// Move value assignment.
optional<T>& operator=(T&& other) { // NOLINT(build/c++11)
if (engaged_) {
value() = std::move(other);
} else {
SetValue(std::move(other));
}
return *this;
}
// Move optional assignment.
optional<T>& operator=(optional&& other) { // NOLINT(build/c++11)
if (engaged_ && other.engaged_) {
value() = std::move(other.value());
} else if (!engaged_ && other.engaged_) {
SetValue(std::move(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)) || \
(defined(__GNUC__) && \
(__GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 5))))
// 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
bool has_engaged() const { return engaged_; }
// 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 {
SB_DCHECK(engaged_)
<< "Attempted to access object in a disengaged optional.";
return *static_cast<const T*>(void_value());
}
T& value() {
SB_DCHECK(engaged_)
<< "Attempted to access object in a disengaged optional.";
return *static_cast<T*>(void_value());
}
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(std::move(value()));
EnsureDisengaged();
} else if (other.engaged_) {
SetValue(std::move(other.value()));
other.EnsureDisengaged();
}
// If both the lhs and rhs are not engaged, we do nothing.
}
// Include the pump.py-generated declaration and implementation for the
// forwarding constructor and emplace.
#include "starboard/common/optional_internal.h"
private:
// Sets a non-engaged optional to a specified value, and marks it as engaged.
void SetValue(T&& value) { // NOLINT(build/c++11)
new (void_value()) T(std::move(value));
engaged_ = true;
#if !defined(NDEBUG)
value_ptr_ = static_cast<const T*>(void_value());
#endif
}
// Sets a non-engaged optional to a specified value, and marks it as engaged.
template <typename U>
void SetValue(const U& value) {
new (void_value()) T(value);
engaged_ = true;
#if !defined(NDEBUG)
value_ptr_ = static_cast<const T*>(void_value());
#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*>(void_value())->~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
}
const void* void_value() const {
return reinterpret_cast<const void*>(value_memory_);
}
void* void_value() { return reinterpret_cast<void*>(value_memory_); }
// The actual memory reserved for the object that may or may not exist.
SB_ALIGNAS(SB_ALIGNOF(T)) uint8_t value_memory_[sizeof(T)];
// 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 starboard
namespace std {
template <typename T>
struct hash<::starboard::optional<T> > {
public:
size_t operator()(const ::starboard::optional<T>& value) const {
return (value ? value_hash_(value.value()) : 0);
}
private:
hash<T> value_hash_;
};
template <typename T>
void swap(::starboard::optional<T>& lhs, ::starboard::optional<T>& rhs) {
lhs.swap(rhs);
}
} // namespace std
#endif // STARBOARD_COMMON_OPTIONAL_H_