// Copyright 2019 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See https://github.com/danielaparker/jsoncons for latest version #ifndef JSONCONS_DETAIL_OPTIONAL_HPP #define JSONCONS_DETAIL_OPTIONAL_HPP #include // placement new #include #include // std::swap #include #include namespace jsoncons { namespace detail { template class optional; template struct is_constructible_or_convertible_from_optional : std::integral_constant< bool, std::is_constructible&>::value || std::is_constructible&&>::value || std::is_constructible&>::value || std::is_constructible&&>::value || std::is_convertible&, T1>::value || std::is_convertible&&, T1>::value || std::is_convertible&, T1>::value || std::is_convertible&&, T1>::value> {}; template struct is_constructible_convertible_or_assignable_from_optional : std::integral_constant< bool, is_constructible_or_convertible_from_optional::value || std::is_assignable&>::value || std::is_assignable&&>::value || std::is_assignable&>::value || std::is_assignable&&>::value> {}; template class optional { public: using value_type = T; private: bool has_value_; union { char dummy_; T value_; }; public: constexpr optional() noexcept : has_value_(false), dummy_{} { } // copy constructors optional(const optional& other) : has_value_(false), dummy_{} { if (other) { construct(*other); } } // converting template ::value && std::is_constructible::value && std::is_convertible::value && !is_constructible_or_convertible_from_optional::value && std::is_copy_constructible::type>::value,int>::type = 0> optional(const optional& other) : has_value_(false), dummy_{} { if (other) { construct(*other); } } template ::value && std::is_constructible::value && !std::is_convertible::value && !is_constructible_or_convertible_from_optional::value && std::is_copy_constructible::type>::value,int>::type = 0> explicit optional(const optional& other) : has_value_(false), dummy_{} { if (other) { construct(*other); } } // move constructors template optional(optional&& other, typename std::enable_if::type>::value>::type* = 0) : has_value_(false), dummy_{} { if (other) { construct(std::move(other.value_)); } } // converting template optional(optional&& value, typename std::enable_if::value && std::is_constructible::value && !is_constructible_or_convertible_from_optional::value && std::is_convertible::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward(value)) { } template explicit optional(optional&& value, typename std::enable_if::value && std::is_constructible::value && !is_constructible_or_convertible_from_optional::value && !std::is_convertible::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward(value)) { } // value constructors template optional(T2&& value, typename std::enable_if,typename std::decay::type>::value && std::is_constructible::value && std::is_convertible::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward(value)) { } template explicit optional(T2&& value, typename std::enable_if,typename std::decay::type>::value && std::is_constructible::value && !std::is_convertible::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward(value)) { } ~optional() noexcept { destroy(); } optional& operator=(const optional& other) { if (other) { assign(*other); } else { reset(); } return *this; } optional& operator=(optional&& other ) { if (other) { assign(std::move(*other)); } else { reset(); } return *this; } template typename std::enable_if, U>::value && std::is_constructible::value && !is_constructible_convertible_or_assignable_from_optional::value && std::is_assignable::value, optional&>::type operator=(const optional& other) { if (other) { assign(*other); } else { destroy(); } return *this; } template typename std::enable_if, U>::value && std::is_constructible::value && !is_constructible_convertible_or_assignable_from_optional::value && std::is_assignable::value, optional&>::type operator=(optional&& other) { if (other) { assign(std::move(*other)); } else { destroy(); } return *this; } // value assignment template typename std::enable_if, typename std::decay::type>::value && std::is_constructible::value && std::is_assignable::value && !(std::is_scalar::value && std::is_same::type>::value), optional&>::type operator=(T2&& v) { assign(std::forward(v)); return *this; } constexpr explicit operator bool() const noexcept { return has_value_; } constexpr bool has_value() const noexcept { return has_value_; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4702) #endif // _MSC_VER T& value() & { return static_cast(*this) ? get() : JSONCONS_THROW(std::runtime_error("Bad optional access")), get(); } constexpr const T& value() const & { return static_cast(*this) ? get() : JSONCONS_THROW(std::runtime_error("Bad optional access")), get(); } template constexpr T value_or(U&& default_value) const & { static_assert(std::is_copy_constructible::value, "get_value_or: T must be copy constructible"); static_assert(std::is_convertible::value, "get_value_or: U must be convertible to T"); return static_cast(*this) ? **this : static_cast(std::forward(default_value)); } template T value_or(U&& default_value) && { static_assert(std::is_move_constructible::value, "get_value_or: T must be move constructible"); static_assert(std::is_convertible::value, "get_value_or: U must be convertible to T"); return static_cast(*this) ? std::move(**this) : static_cast(std::forward(default_value)); } #ifdef _MSC_VER #pragma warning(pop) #endif // _MSC_VER const T* operator->() const { return std::addressof(this->value_); } T* operator->() { return std::addressof(this->value_); } constexpr const T& operator*() const& { return value(); } T& operator*() & { return value(); } void reset() noexcept { destroy(); } void swap(optional& other) noexcept(std::is_nothrow_move_constructible::value /*&& std::is_nothrow_swappable::value*/) { const bool contains_a_value = has_value(); if (contains_a_value == other.has_value()) { if (contains_a_value) { using std::swap; swap(**this, *other); } } else { optional& source = contains_a_value ? *this : other; optional& target = contains_a_value ? other : *this; target = optional(*source); source.reset(); } } private: constexpr const T& get() const { return this->value_; } T& get() { return this->value_; } template void construct(Args&&... args) { ::new (static_cast(&this->value_)) T(std::forward(args)...); has_value_ = true; } void destroy() noexcept { if (has_value_) { value_.~T(); has_value_ = false; } } template void assign(U&& u) { if (has_value_) { value_ = std::forward(u); } else { construct(std::forward(u)); } } }; template typename std::enable_if::value,void>::type swap(optional& lhs, optional& rhs) noexcept { lhs.swap(rhs); } template constexpr bool operator==(const optional& lhs, const optional& rhs) noexcept { return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); } template constexpr bool operator!=(const optional& lhs, const optional& rhs) noexcept { return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); } template constexpr bool operator<(const optional& lhs, const optional& rhs) noexcept { return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); } template constexpr bool operator>(const optional& lhs, const optional& rhs) noexcept { return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); } template constexpr bool operator<=(const optional& lhs, const optional& rhs) noexcept { return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); } template constexpr bool operator>=(const optional& lhs, const optional& rhs) noexcept { return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); } template constexpr bool operator==(const optional& lhs, const T2& rhs) noexcept { return lhs ? *lhs == rhs : false; } template constexpr bool operator==(const T1& lhs, const optional& rhs) noexcept { return rhs ? lhs == *rhs : false; } template constexpr bool operator!=(const optional& lhs, const T2& rhs) noexcept { return lhs ? *lhs != rhs : true; } template constexpr bool operator!=(const T1& lhs, const optional& rhs) noexcept { return rhs ? lhs != *rhs : true; } template constexpr bool operator<(const optional& lhs, const T2& rhs) noexcept { return lhs ? *lhs < rhs : true; } template constexpr bool operator<(const T1& lhs, const optional& rhs) noexcept { return rhs ? lhs < *rhs : false; } template constexpr bool operator<=(const optional& lhs, const T2& rhs) noexcept { return lhs ? *lhs <= rhs : true; } template constexpr bool operator<=(const T1& lhs, const optional& rhs) noexcept { return rhs ? lhs <= *rhs : false; } template constexpr bool operator>(const optional& lhs, const T2& rhs) noexcept { return lhs ? *lhs > rhs : false; } template constexpr bool operator>(const T1& lhs, const optional& rhs) noexcept { return rhs ? lhs > *rhs : true; } template constexpr bool operator>=(const optional& lhs, const T2& rhs) noexcept { return lhs ? *lhs >= rhs : false; } template constexpr bool operator>=(const T1& lhs, const optional& rhs) noexcept { return rhs ? lhs >= *rhs : true; } } // namespace detail } // namespace jsoncons #endif