// -*-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 }