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