// -*-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. */ #ifndef cm_optional #define cm_optional #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define CMake_HAVE_CXX_OPTIONAL #endif #if defined(CMake_HAVE_CXX_OPTIONAL) # include // IWYU pragma: export #else # include # include #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 class optional { public: using value_type = T; optional() noexcept = default; optional(nullopt_t) noexcept; optional(const optional& other); optional(optional&& other) noexcept; template explicit optional(cm::in_place_t, Args&&... args); template < typename U = T, typename = typename std::enable_if< std::is_constructible::value && !std::is_same::type, cm::in_place_t>::value && !std::is_same::type, cm::optional>::value>::type> optional(U&& v); ~optional(); optional& operator=(nullopt_t) noexcept; optional& operator=(const optional& other); optional& operator=(optional&& other) noexcept; template < typename U = T, typename = typename std::enable_if< !std::is_same::type, cm::optional>::value && std::is_constructible::value && std::is_assignable::value && (!std::is_scalar::value || !std::is_same::type, T>::value)>::type> optional& 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 T value_or(U&& default_value) const&; template T value_or(U&& default_value) &&; void swap(optional& other) noexcept; void reset() noexcept; template T& emplace(Args&&... args); private: bool _has_value = false; std::allocator _allocator; union _mem_union { T value; // Explicit constructor and destructor is required to make this work _mem_union() noexcept {} ~_mem_union() noexcept {} } _mem; }; template optional::type> make_optional(T&& value) { return optional::type>(std::forward(value)); } template optional make_optional(Args&&... args) { return optional(in_place, std::forward(args)...); } template optional::optional(nullopt_t) noexcept { } template optional::optional(const optional& other) { *this = other; } template optional::optional(optional&& other) noexcept { *this = std::move(other); } template template optional::optional(cm::in_place_t, Args&&... args) { this->emplace(std::forward(args)...); } template template optional::optional(U&& v) { this->emplace(std::forward(v)); } template optional::~optional() { this->reset(); } template optional& optional::operator=(nullopt_t) noexcept { this->reset(); return *this; } template optional& optional::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 optional& optional::operator=(optional&& 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 template optional& optional::operator=(U&& v) { if (this->has_value()) { this->value() = v; } else { this->emplace(std::forward(v)); } return *this; } template const T* optional::operator->() const { return &**this; } template T* optional::operator->() { return &**this; } template const T& optional::operator*() const& { return this->_mem.value; } template T& optional::operator*() & { return this->_mem.value; } template const T&& optional::operator*() const&& { return std::move(**this); } template T&& optional::operator*() && { return std::move(**this); } template bool optional::has_value() const noexcept { return this->_has_value; } template optional::operator bool() const noexcept { return this->has_value(); } template T& optional::value() & { if (!this->has_value()) { throw cm::bad_optional_access{}; } return **this; } template const T& optional::value() const& { if (!this->has_value()) { throw cm::bad_optional_access{}; } return **this; } template template T optional::value_or(U&& default_value) const& { return bool(*this) ? **this : static_cast(std::forward(default_value)); } template template T optional::value_or(U&& default_value) && { return bool(*this) ? std::move(**this) : static_cast(std::forward(default_value)); } template void optional::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 void optional::reset() noexcept { if (this->has_value()) { this->_has_value = false; std::allocator_traits>::destroy(this->_allocator, &**this); } } template template T& optional::emplace(Args&&... args) { this->reset(); std::allocator_traits>::construct( this->_allocator, &**this, std::forward(args)...); this->_has_value = true; return this->value(); } #endif } #endif