blob: 52c96fac6b653330eb7219b2c9f64f50372c1aea [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_FIT_OPTIONAL_H_
#define LIB_FIT_OPTIONAL_H_
#include <assert.h>
#include <new>
#include <type_traits>
#include <utility>
namespace fit {
namespace internal {
template <typename T, bool = std::is_assignable<T&, const T&>::value>
struct copy_assign_or_reconstruct final {
static void assign(T* dest, const T& source) {
dest->~T();
new (dest) T(source);
}
};
template <typename T>
struct copy_assign_or_reconstruct<T, true> final {
static void assign(T* dest, const T& source) {
*dest = source;
}
};
template <typename T, bool = std::is_assignable<T&, T&&>::value>
struct move_assign_or_reconstruct final {
static void assign(T* dest, T&& source) {
dest->~T();
new (dest) T(std::move(source));
}
static void swap(T& a, T& b) {
T temp(std::move(a));
a.~T();
new (&a) T(std::move(b));
b.~T();
new (&b) T(std::move(temp));
}
};
template <typename T>
struct move_assign_or_reconstruct<T, true> final {
static void assign(T* dest, T&& source) {
*dest = std::move(source);
}
static void swap(T& a, T& b) {
using std::swap;
swap(a, b);
}
};
} // namespace internal
// A sentinel value for |fit::optional<T>| indicating that it contains
// no value.
struct nullopt_t {
explicit constexpr nullopt_t(int) {}
};
static constexpr nullopt_t nullopt(0);
// A minimal implementation of an |std::optional<T>| work-alike for C++ 14.
//
// See also |fit::nullable<T>| which may be more efficient in certain
// circumstances if T can be initialized, assigned, and compared with
// nullptr.
//
// TODO(US-90): The initial implementation only covers a minimal subset of the
// std::optional API. Flesh this out more fully then define fit::optional
// to be an alias for std::optional when compiling with C++ 17.
template <typename T>
class optional final {
public:
using value_type = T;
constexpr optional()
: has_value_(false) {}
constexpr optional(nullopt_t)
: has_value_(false) {}
constexpr optional(T value)
: has_value_(true), value_(std::move(value)) {}
optional(const optional& other)
: has_value_(other.has_value_) {
if (has_value_) {
new (&value_) T(other.value_);
}
}
optional(optional&& other)
: has_value_(other.has_value_) {
if (has_value_) {
new (&value_) T(std::move(other.value_));
other.value_.~T();
other.has_value_ = false;
}
}
// TODO(US-90): Presence of this destructor makes the type non-literal.
// We should specialize this type to handle the case where T is literal
// explicitly so that expressions these types can be constexpr.
~optional() {
if (has_value_) {
value_.~T();
}
}
constexpr T& value() & {
assert(has_value_);
return value_;
}
constexpr const T& value() const& {
assert(has_value_);
return value_;
}
constexpr T&& value() && {
assert(has_value_);
return std::move(value_);
}
constexpr const T&& value() const&& {
assert(has_value_);
return std::move(value_);
}
template <typename U = T>
constexpr T value_or(U&& default_value) const {
return has_value_ ? value_ : static_cast<T>(std::forward<U>(default_value));
}
constexpr T* operator->() { return &value_; }
constexpr const T* operator->() const { return &value_; }
constexpr T& operator*() { return value_; }
constexpr const T& operator*() const { return value_; }
bool has_value() const { return has_value_; }
explicit operator bool() const { return has_value(); }
optional& operator=(const optional& other) {
if (&other == this)
return *this;
if (has_value_) {
if (other.has_value_) {
::fit::internal::copy_assign_or_reconstruct<T>::assign(
&value_, other.value_);
} else {
reset();
}
} else if (other.has_value_) {
new (&value_) T(other.value_);
has_value_ = true;
}
return *this;
}
optional& operator=(optional&& other) {
if (&other == this)
return *this;
if (has_value_) {
if (other.has_value_) {
::fit::internal::move_assign_or_reconstruct<T>::assign(
&value_, std::move(other.value_));
other.value_.~T();
other.has_value_ = false;
} else {
reset();
}
} else if (other.has_value_) {
new (&value_) T(std::move(other.value_));
has_value_ = true;
other.value_.~T();
other.has_value_ = false;
}
return *this;
}
optional& operator=(nullopt_t) {
reset();
return *this;
}
optional& operator=(T value) {
if (has_value_) {
::fit::internal::move_assign_or_reconstruct<T>::assign(
&value_, std::move(value));
} else {
new (&value_) T(std::move(value));
has_value_ = true;
}
return *this;
}
void reset() {
if (has_value_) {
value_.~T();
has_value_ = false;
}
}
void swap(optional& other) {
if (&other == this)
return;
if (has_value_) {
if (other.has_value_) {
::fit::internal::move_assign_or_reconstruct<T>::swap(
value_, other.value_);
} else {
new (&other.value_) T(std::move(value_));
other.has_value_ = true;
value_.~T();
has_value_ = false;
}
} else if (other.has_value_) {
new (&value_) T(std::move(other.value_));
has_value_ = true;
other.value_.~T();
other.has_value_ = false;
}
}
template <typename... Args>
T& emplace(Args&&... args) {
reset();
new (&value_) T(std::forward<Args...>(args)...);
has_value_ = true;
return value_;
}
private:
bool has_value_;
union {
T value_;
};
};
template <typename T>
void swap(optional<T>& a, optional<T>& b) {
a.swap(b);
}
template <typename T>
constexpr bool operator==(const optional<T>& lhs, nullopt_t) {
return !lhs.has_value();
}
template <typename T>
constexpr bool operator!=(const optional<T>& lhs, nullopt_t) {
return lhs.has_value();
}
template <typename T>
constexpr bool operator==(nullopt_t, const optional<T>& rhs) {
return !rhs.has_value();
}
template <typename T>
constexpr bool operator!=(nullopt_t, const optional<T>& rhs) {
return rhs.has_value();
}
template <typename T, typename U>
constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
return (lhs.has_value() == rhs.has_value()) && (!lhs.has_value() || *lhs == *rhs);
}
template <typename T, typename U>
constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
return (lhs.has_value() != rhs.has_value()) || (lhs.has_value() && *lhs != *rhs);
}
template <typename T, typename U>
constexpr bool operator==(const optional<T>& lhs, const U& rhs) {
return lhs.has_value() && *lhs == rhs;
}
template <typename T, typename U>
constexpr bool operator!=(const optional<T>& lhs, const U& rhs) {
return !lhs.has_value() || *lhs != rhs;
}
template <typename T, typename U>
constexpr bool operator==(const T& lhs, const optional<U>& rhs) {
return rhs.has_value() && lhs == *rhs;
}
template <typename T, typename U>
constexpr bool operator!=(const T& lhs, const optional<U>& rhs) {
return !rhs.has_value() || lhs != *rhs;
}
} // namespace fit
#endif // LIB_FIT_OPTIONAL_H_