You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
568 lines
12 KiB
568 lines
12 KiB
// -*-c++-*-
|
|
// vim: set ft=cpp:
|
|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#pragma once
|
|
|
|
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
|
# define CMake_HAVE_CXX_OPTIONAL
|
|
#endif
|
|
|
|
#if defined(CMake_HAVE_CXX_OPTIONAL)
|
|
# include <optional> // IWYU pragma: export
|
|
#else
|
|
# include <memory>
|
|
|
|
# include <cm/utility>
|
|
#endif
|
|
|
|
namespace cm {
|
|
|
|
#if defined(CMake_HAVE_CXX_OPTIONAL)
|
|
|
|
using std::nullopt_t;
|
|
using std::nullopt;
|
|
using std::optional;
|
|
using std::bad_optional_access;
|
|
using std::make_optional;
|
|
|
|
#else
|
|
|
|
class bad_optional_access : public std::exception
|
|
{
|
|
using std::exception::exception;
|
|
};
|
|
|
|
struct nullopt_t
|
|
{
|
|
explicit constexpr nullopt_t(int) {}
|
|
};
|
|
|
|
constexpr nullopt_t nullopt{ 0 };
|
|
|
|
template <typename T>
|
|
class optional
|
|
{
|
|
public:
|
|
using value_type = T;
|
|
|
|
optional() noexcept = default;
|
|
optional(nullopt_t) noexcept;
|
|
optional(const optional& other);
|
|
optional(optional&& other) noexcept;
|
|
|
|
template <typename... Args>
|
|
explicit optional(cm::in_place_t, Args&&... args);
|
|
|
|
template <
|
|
typename U = T,
|
|
typename = typename std::enable_if<
|
|
std::is_constructible<T, U&&>::value &&
|
|
!std::is_same<typename std::decay<U>::type, cm::in_place_t>::value &&
|
|
!std::is_same<typename std::decay<U>::type,
|
|
cm::optional<T>>::value>::type>
|
|
optional(U&& v);
|
|
|
|
~optional();
|
|
|
|
optional& operator=(nullopt_t) noexcept;
|
|
optional& operator=(const optional& other);
|
|
|
|
template <typename U = T>
|
|
typename std::enable_if<std::is_constructible<T, U&&>::value &&
|
|
std::is_assignable<T&, U&&>::value,
|
|
optional&>::type
|
|
operator=(optional<U>&& other) noexcept;
|
|
|
|
template <typename U = T>
|
|
typename std::enable_if<
|
|
!std::is_same<typename std::decay<U>::type, cm::optional<T>>::value &&
|
|
std::is_constructible<T, U&&>::value &&
|
|
std::is_assignable<T&, U&&>::value &&
|
|
(!std::is_scalar<T>::value ||
|
|
!std::is_same<typename std::decay<U>::type, T>::value),
|
|
optional&>::type
|
|
operator=(U&& v);
|
|
|
|
const T* operator->() const;
|
|
T* operator->();
|
|
const T& operator*() const&;
|
|
T& operator*() &;
|
|
const T&& operator*() const&&;
|
|
T&& operator*() &&;
|
|
|
|
explicit operator bool() const noexcept;
|
|
bool has_value() const noexcept;
|
|
|
|
T& value() &;
|
|
const T& value() const&;
|
|
|
|
T&& value() &&;
|
|
const T&& value() const&&;
|
|
|
|
template <typename U>
|
|
T value_or(U&& default_value) const&;
|
|
|
|
template <typename U>
|
|
T value_or(U&& default_value) &&;
|
|
|
|
void swap(optional& other) noexcept;
|
|
void reset() noexcept;
|
|
|
|
template <typename... Args>
|
|
T& emplace(Args&&... args);
|
|
|
|
private:
|
|
bool _has_value = false;
|
|
std::allocator<T> _allocator;
|
|
union _mem_union
|
|
{
|
|
T value;
|
|
|
|
// Explicit constructor and destructor is required to make this work
|
|
_mem_union() noexcept {}
|
|
~_mem_union() noexcept {}
|
|
} _mem;
|
|
};
|
|
|
|
template <typename T>
|
|
optional<typename std::decay<T>::type> make_optional(T&& value)
|
|
{
|
|
return optional<typename std::decay<T>::type>(std::forward<T>(value));
|
|
}
|
|
|
|
template <typename T, class... Args>
|
|
optional<T> make_optional(Args&&... args)
|
|
{
|
|
return optional<T>(in_place, std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename T>
|
|
optional<T>::optional(nullopt_t) noexcept
|
|
: optional()
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
optional<T>::optional(const optional& other)
|
|
{
|
|
if (other.has_value()) {
|
|
this->emplace(*other);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
optional<T>::optional(optional&& other) noexcept
|
|
{
|
|
if (other.has_value()) {
|
|
this->emplace(std::move(*other));
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename... Args>
|
|
optional<T>::optional(cm::in_place_t, Args&&... args)
|
|
{
|
|
this->emplace(std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U, typename>
|
|
optional<T>::optional(U&& v)
|
|
{
|
|
this->emplace(std::forward<U>(v));
|
|
}
|
|
|
|
template <typename T>
|
|
optional<T>::~optional()
|
|
{
|
|
this->reset();
|
|
}
|
|
|
|
template <typename T>
|
|
optional<T>& optional<T>::operator=(nullopt_t) noexcept
|
|
{
|
|
this->reset();
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
optional<T>& optional<T>::operator=(const optional& other)
|
|
{
|
|
if (other.has_value()) {
|
|
if (this->has_value()) {
|
|
this->value() = *other;
|
|
} else {
|
|
this->emplace(*other);
|
|
}
|
|
} else {
|
|
this->reset();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
typename std::enable_if<std::is_constructible<T, U&&>::value &&
|
|
std::is_assignable<T&, U&&>::value,
|
|
optional<T>&>::type
|
|
optional<T>::operator=(optional<U>&& other) noexcept
|
|
{
|
|
if (other.has_value()) {
|
|
if (this->has_value()) {
|
|
this->value() = std::move(*other);
|
|
} else {
|
|
this->emplace(std::move(*other));
|
|
}
|
|
} else {
|
|
this->reset();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
typename std::enable_if<
|
|
!std::is_same<typename std::decay<U>::type, cm::optional<T>>::value &&
|
|
std::is_constructible<T, U&&>::value &&
|
|
std::is_assignable<T&, U&&>::value &&
|
|
(!std::is_scalar<T>::value ||
|
|
!std::is_same<typename std::decay<U>::type, T>::value),
|
|
optional<T>&>::type
|
|
optional<T>::operator=(U&& v)
|
|
{
|
|
if (this->has_value()) {
|
|
this->value() = v;
|
|
} else {
|
|
this->emplace(std::forward<U>(v));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator==(const optional<T>& lhs, const optional<U>& rhs)
|
|
{
|
|
if (lhs.has_value()) {
|
|
return rhs.has_value() && *lhs == *rhs;
|
|
}
|
|
return !rhs.has_value();
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator!=(const optional<T>& lhs, const optional<U>& rhs)
|
|
{
|
|
if (lhs.has_value()) {
|
|
return !rhs.has_value() || *lhs != *rhs;
|
|
}
|
|
return rhs.has_value();
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator<(const optional<T>& lhs, const optional<U>& rhs)
|
|
{
|
|
if (rhs.has_value()) {
|
|
return !lhs.has_value() || *lhs < *rhs;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator<=(const optional<T>& lhs, const optional<U>& rhs)
|
|
{
|
|
if (!lhs.has_value()) {
|
|
return true;
|
|
}
|
|
if (rhs.has_value()) {
|
|
return *lhs <= *rhs;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator>(const optional<T>& lhs, const optional<U>& rhs)
|
|
{
|
|
if (lhs.has_value()) {
|
|
return !rhs.has_value() || *lhs > *rhs;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator>=(const optional<T>& lhs, const optional<U>& rhs)
|
|
{
|
|
if (!rhs.has_value()) {
|
|
return true;
|
|
}
|
|
if (lhs.has_value()) {
|
|
return *lhs >= *rhs;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator==(const optional<T>& opt, nullopt_t) noexcept
|
|
{
|
|
return !opt.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator!=(const optional<T>& opt, nullopt_t) noexcept
|
|
{
|
|
return opt.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator<(const optional<T>& /*opt*/, nullopt_t) noexcept
|
|
{
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator<=(const optional<T>& opt, nullopt_t) noexcept
|
|
{
|
|
return !opt.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator>(const optional<T>& opt, nullopt_t) noexcept
|
|
{
|
|
return opt.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator>=(const optional<T>& /*opt*/, nullopt_t) noexcept
|
|
{
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator==(nullopt_t, const optional<T>& opt) noexcept
|
|
{
|
|
return !opt.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator!=(nullopt_t, const optional<T>& opt) noexcept
|
|
{
|
|
return opt.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator<(nullopt_t, const optional<T>& opt) noexcept
|
|
{
|
|
return opt.has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator<=(nullopt_t, const optional<T>& /*opt*/) noexcept
|
|
{
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator>(nullopt_t, const optional<T>& /*opt*/) noexcept
|
|
{
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator>=(nullopt_t, const optional<T>& opt) noexcept
|
|
{
|
|
return !opt.has_value();
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator==(const optional<T>& opt, const U& value)
|
|
{
|
|
return opt.has_value() && *opt == value;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator!=(const optional<T>& opt, const U& value)
|
|
{
|
|
return !opt.has_value() || *opt != value;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator<(const optional<T>& opt, const U& value)
|
|
{
|
|
return !opt.has_value() || *opt < value;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator<=(const optional<T>& opt, const U& value)
|
|
{
|
|
return !opt.has_value() || *opt <= value;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator>(const optional<T>& opt, const U& value)
|
|
{
|
|
return opt.has_value() && *opt > value;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator>=(const optional<T>& opt, const U& value)
|
|
{
|
|
return opt.has_value() && *opt >= value;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator==(const T& value, const optional<U>& opt)
|
|
{
|
|
return opt.has_value() && value == *opt;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator!=(const T& value, const optional<U>& opt)
|
|
{
|
|
return !opt.has_value() || value != *opt;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator<(const T& value, const optional<U>& opt)
|
|
{
|
|
return opt.has_value() && value < *opt;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator<=(const T& value, const optional<U>& opt)
|
|
{
|
|
return opt.has_value() && value <= *opt;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator>(const T& value, const optional<U>& opt)
|
|
{
|
|
return !opt.has_value() || value > *opt;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
bool operator>=(const T& value, const optional<U>& opt)
|
|
{
|
|
return !opt.has_value() || value >= *opt;
|
|
}
|
|
|
|
template <typename T>
|
|
const T* optional<T>::operator->() const
|
|
{
|
|
return &**this;
|
|
}
|
|
|
|
template <typename T>
|
|
T* optional<T>::operator->()
|
|
{
|
|
return &**this;
|
|
}
|
|
|
|
template <typename T>
|
|
const T& optional<T>::operator*() const&
|
|
{
|
|
return this->_mem.value;
|
|
}
|
|
|
|
template <typename T>
|
|
T& optional<T>::operator*() &
|
|
{
|
|
return this->_mem.value;
|
|
}
|
|
|
|
template <typename T>
|
|
const T&& optional<T>::operator*() const&&
|
|
{
|
|
return std::move(**this);
|
|
}
|
|
|
|
template <typename T>
|
|
T&& optional<T>::operator*() &&
|
|
{
|
|
return std::move(**this);
|
|
}
|
|
|
|
template <typename T>
|
|
bool optional<T>::has_value() const noexcept
|
|
{
|
|
return this->_has_value;
|
|
}
|
|
|
|
template <typename T>
|
|
optional<T>::operator bool() const noexcept
|
|
{
|
|
return this->has_value();
|
|
}
|
|
|
|
template <typename T>
|
|
T& optional<T>::value() &
|
|
{
|
|
if (!this->has_value()) {
|
|
throw cm::bad_optional_access{};
|
|
}
|
|
return **this;
|
|
}
|
|
|
|
template <typename T>
|
|
const T& optional<T>::value() const&
|
|
{
|
|
if (!this->has_value()) {
|
|
throw cm::bad_optional_access{};
|
|
}
|
|
return **this;
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
T optional<T>::value_or(U&& default_value) const&
|
|
{
|
|
return bool(*this) ? **this : static_cast<T>(std::forward<U>(default_value));
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename U>
|
|
T optional<T>::value_or(U&& default_value) &&
|
|
{
|
|
return bool(*this) ? std::move(**this)
|
|
: static_cast<T>(std::forward<U>(default_value));
|
|
}
|
|
|
|
template <typename T>
|
|
void optional<T>::swap(optional& other) noexcept
|
|
{
|
|
if (this->has_value()) {
|
|
if (other.has_value()) {
|
|
using std::swap;
|
|
swap(**this, *other);
|
|
} else {
|
|
other.emplace(std::move(**this));
|
|
this->reset();
|
|
}
|
|
} else if (other.has_value()) {
|
|
this->emplace(std::move(*other));
|
|
other.reset();
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void optional<T>::reset() noexcept
|
|
{
|
|
if (this->has_value()) {
|
|
this->_has_value = false;
|
|
std::allocator_traits<std::allocator<T>>::destroy(this->_allocator,
|
|
&**this);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
template <typename... Args>
|
|
T& optional<T>::emplace(Args&&... args)
|
|
{
|
|
this->reset();
|
|
std::allocator_traits<std::allocator<T>>::construct(
|
|
this->_allocator, &**this, std::forward<Args>(args)...);
|
|
this->_has_value = true;
|
|
return this->value();
|
|
}
|
|
|
|
#endif
|
|
}
|