blob: 9a0240897c3229a970ad90845d37223b53c356f9 [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.
#pragma once
#include <assert.h>
#include <fbl/new.h>
#include <fbl/type_support.h>
namespace fbl {
namespace {
template <typename T>
void swap(T& t1, T& t2) {
T temp(fbl::move(t1));
t1 = fbl::move(t2);
t2 = fbl::move(temp);
}
} // namespace
// A sentinel value for |fbl::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.
template <typename T>
class optional final {
public:
constexpr optional()
: has_value_(false) {}
explicit constexpr optional(nullopt_t)
: has_value_(false) {}
explicit constexpr optional(T value)
: has_value_(true), value_(fbl::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(fbl::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_;
}
template <typename U = T>
constexpr T value_or(U&& default_value) const {
return has_value_ ? value_ : static_cast<T>(fbl::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_) {
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_) {
value_ = fbl::move(other.value_);
other.value_.~T();
other.has_value_ = false;
} else {
reset();
}
} else if (other.has_value_) {
new (&value_) T(fbl::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_) {
value_ = fbl::move(value);
} else {
new (&value_) T(fbl::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_) {
fbl::swap(value_, other.value_);
} else {
new (&other.value_) T(fbl::move(value_));
other.has_value_ = true;
value_.~T();
has_value_ = false;
}
} else if (other.has_value_) {
new (&value_) T(fbl::move(other.value_));
has_value_ = true;
other.value_.~T();
other.has_value_ = false;
}
}
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 fbl