blob: 06b298cd9b013ca824b824b37d1e88cfb0b0a53d [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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 dap_optional_h
#define dap_optional_h
#include <assert.h>
#include <type_traits>
#include <utility> // std::move
namespace dap {
// optional holds an 'optional' contained value.
// This is similar to C++17's std::optional.
template <typename T>
class optional {
template <typename U>
using IsConvertibleToT =
typename std::enable_if<std::is_convertible<U, T>::value>::type;
public:
using value_type = T;
// constructors
inline optional() = default;
inline optional(const optional& other);
inline optional(optional&& other);
template <typename U>
inline optional(const optional<U>& other);
template <typename U>
inline optional(optional<U>&& other);
template <typename U = value_type, typename = IsConvertibleToT<U>>
inline optional(U&& value);
// value() returns the contained value.
// If the optional does not contain a value, then value() will assert.
inline T& value();
inline const T& value() const;
// value() returns the contained value, or defaultValue if the optional does
// not contain a value.
inline const T& value(const T& defaultValue) const;
// operator bool() returns true if the optional contains a value.
inline explicit operator bool() const noexcept;
// has_value() returns true if the optional contains a value.
inline bool has_value() const;
// assignment
inline optional& operator=(const optional& other);
inline optional& operator=(optional&& other) noexcept;
template <typename U = T, typename = IsConvertibleToT<U>>
inline optional& operator=(U&& value);
template <typename U>
inline optional& operator=(const optional<U>& other);
template <typename U>
inline optional& operator=(optional<U>&& other);
// value access
inline const T* operator->() const;
inline T* operator->();
inline const T& operator*() const;
inline T& operator*();
private:
T val = {};
bool set = false;
};
template <typename T>
optional<T>::optional(const optional& other) : val(other.val), set(other.set) {}
template <typename T>
optional<T>::optional(optional&& other)
: val(std::move(other.val)), set(other.set) {}
template <typename T>
template <typename U>
optional<T>::optional(const optional<U>& other) : set(other.has_value()) {
if (set) {
val = static_cast<T>(other.value());
}
}
template <typename T>
template <typename U>
optional<T>::optional(optional<U>&& other) : set(other.has_value()) {
if (set) {
val = static_cast<T>(std::move(other.value()));
}
}
template <typename T>
template <typename U /*= T*/, typename>
optional<T>::optional(U&& value) : val(std::move(value)), set(true) {}
template <typename T>
T& optional<T>::value() {
assert(set);
return val;
}
template <typename T>
const T& optional<T>::value() const {
assert(set);
return val;
}
template <typename T>
const T& optional<T>::value(const T& defaultValue) const {
if (!has_value()) {
return defaultValue;
}
return val;
}
template <typename T>
optional<T>::operator bool() const noexcept {
return set;
}
template <typename T>
bool optional<T>::has_value() const {
return set;
}
template <typename T>
optional<T>& optional<T>::operator=(const optional& other) {
val = other.val;
set = other.set;
return *this;
}
template <typename T>
optional<T>& optional<T>::operator=(optional&& other) noexcept {
val = std::move(other.val);
set = other.set;
return *this;
}
template <typename T>
template <typename U /* = T */, typename>
optional<T>& optional<T>::operator=(U&& value) {
val = std::move(value);
set = true;
return *this;
}
template <typename T>
template <typename U>
optional<T>& optional<T>::operator=(const optional<U>& other) {
val = other.val;
set = other.set;
return *this;
}
template <typename T>
template <typename U>
optional<T>& optional<T>::operator=(optional<U>&& other) {
val = std::move(other.val);
set = other.set;
return *this;
}
template <typename T>
const T* optional<T>::operator->() const {
assert(set);
return &val;
}
template <typename T>
T* optional<T>::operator->() {
assert(set);
return &val;
}
template <typename T>
const T& optional<T>::operator*() const {
assert(set);
return val;
}
template <typename T>
T& optional<T>::operator*() {
assert(set);
return val;
}
template <class T, class U>
inline bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value() && !rhs.has_value()) {
return true;
}
if (!lhs.has_value() || !rhs.has_value()) {
return false;
}
return lhs.value() == rhs.value();
}
template <class T, class U>
inline bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
return !(lhs == rhs);
}
template <class T, class U>
inline bool operator<(const optional<T>& lhs, const optional<U>& rhs) {
if (!rhs.has_value()) {
return false;
}
if (!lhs.has_value()) {
return true;
}
return lhs.value() < rhs.value();
}
template <class T, class U>
inline bool operator<=(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value()) {
return true;
}
if (!rhs.has_value()) {
return false;
}
return lhs.value() <= rhs.value();
}
template <class T, class U>
inline bool operator>(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value()) {
return false;
}
if (!rhs.has_value()) {
return true;
}
return lhs.value() > rhs.value();
}
template <class T, class U>
inline bool operator>=(const optional<T>& lhs, const optional<U>& rhs) {
if (!rhs.has_value()) {
return true;
}
if (!lhs.has_value()) {
return false;
}
return lhs.value() >= rhs.value();
}
} // namespace dap
#endif // dap_optional_h