// 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 <new> // placement new #include <memory> #include <utility> // std::swap #include <type_traits> #include <jsoncons/config/compiler_support.hpp> namespace jsoncons { namespace detail { template <typename T> class optional; template <typename T1, typename T2> struct is_constructible_or_convertible_from_optional : std::integral_constant< bool, std::is_constructible<T1, optional<T2>&>::value || std::is_constructible<T1, optional<T2>&&>::value || std::is_constructible<T1, const optional<T2>&>::value || std::is_constructible<T1, const optional<T2>&&>::value || std::is_convertible<optional<T2>&, T1>::value || std::is_convertible<optional<T2>&&, T1>::value || std::is_convertible<const optional<T2>&, T1>::value || std::is_convertible<const optional<T2>&&, T1>::value> {}; template <typename T1, typename T2> struct is_constructible_convertible_or_assignable_from_optional : std::integral_constant< bool, is_constructible_or_convertible_from_optional<T1, T2>::value || std::is_assignable<T1&, optional<T2>&>::value || std::is_assignable<T1&, optional<T2>&&>::value || std::is_assignable<T1&, const optional<T2>&>::value || std::is_assignable<T1&, const optional<T2>&&>::value> {}; template <typename T> 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<T>& other) : has_value_(false), dummy_{} { if (other) { construct(*other); } } // converting template <class U, typename std::enable_if<!std::is_same<T,U>::value && std::is_constructible<T, const U&>::value && std::is_convertible<const U&,T>::value && !is_constructible_or_convertible_from_optional<T,U>::value && std::is_copy_constructible<typename std::decay<U>::type>::value,int>::type = 0> optional(const optional<U>& other) : has_value_(false), dummy_{} { if (other) { construct(*other); } } template <class U, typename std::enable_if<!std::is_same<T,U>::value && std::is_constructible<T, const U&>::value && !std::is_convertible<const U&,T>::value && !is_constructible_or_convertible_from_optional<T,U>::value && std::is_copy_constructible<typename std::decay<U>::type>::value,int>::type = 0> explicit optional(const optional<U>& other) : has_value_(false), dummy_{} { if (other) { construct(*other); } } // move constructors template <class T2 = T> optional(optional<T>&& other, typename std::enable_if<std::is_move_constructible<typename std::decay<T2>::type>::value>::type* = 0) : has_value_(false), dummy_{} { if (other) { construct(std::move(other.value_)); } } // converting template <class U> optional(optional<U>&& value, typename std::enable_if<!std::is_same<T,U>::value && std::is_constructible<T, U&&>::value && !is_constructible_or_convertible_from_optional<T,U>::value && std::is_convertible<U&&,T>::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward<U>(value)) { } template <class U> explicit optional(optional<U>&& value, typename std::enable_if<!std::is_same<T,U>::value && std::is_constructible<T, U&&>::value && !is_constructible_or_convertible_from_optional<T,U>::value && !std::is_convertible<U&&,T>::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward<U>(value)) { } // value constructors template <class T2> optional(T2&& value, typename std::enable_if<!std::is_same<optional<T>,typename std::decay<T2>::type>::value && std::is_constructible<T, T2>::value && std::is_convertible<T2,T>::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward<T2>(value)) { } template <class T2> explicit optional(T2&& value, typename std::enable_if<!std::is_same<optional<T>,typename std::decay<T2>::type>::value && std::is_constructible<T, T2>::value && !std::is_convertible<T2,T>::value,int>::type = 0) // (8) : has_value_(true), value_(std::forward<T2>(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 U> typename std::enable_if<!std::is_same<optional<T>, U>::value && std::is_constructible<T, const U&>::value && !is_constructible_convertible_or_assignable_from_optional<T,U>::value && std::is_assignable<T&, const U&>::value, optional&>::type operator=(const optional<U>& other) { if (other) { assign(*other); } else { destroy(); } return *this; } template <typename U> typename std::enable_if<!std::is_same<optional<T>, U>::value && std::is_constructible<T, U>::value && !is_constructible_convertible_or_assignable_from_optional<T,U>::value && std::is_assignable<T&, U>::value, optional&>::type operator=(optional<U>&& other) { if (other) { assign(std::move(*other)); } else { destroy(); } return *this; } // value assignment template <typename T2> typename std::enable_if<!std::is_same<optional<T>, typename std::decay<T2>::type>::value && std::is_constructible<T, T2>::value && std::is_assignable<T&, T2>::value && !(std::is_scalar<T>::value && std::is_same<T, typename std::decay<T2>::type>::value), optional&>::type operator=(T2&& v) { assign(std::forward<T2>(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<bool>(*this) ? get() : JSONCONS_THROW(std::runtime_error("Bad optional access")), get(); } constexpr const T& value() const & { return static_cast<bool>(*this) ? get() : JSONCONS_THROW(std::runtime_error("Bad optional access")), get(); } template <typename U> constexpr T value_or(U&& default_value) const & { static_assert(std::is_copy_constructible<T>::value, "get_value_or: T must be copy constructible"); static_assert(std::is_convertible<U&&, T>::value, "get_value_or: U must be convertible to T"); return static_cast<bool>(*this) ? **this : static_cast<T>(std::forward<U>(default_value)); } template <typename U> T value_or(U&& default_value) && { static_assert(std::is_move_constructible<T>::value, "get_value_or: T must be move constructible"); static_assert(std::is_convertible<U&&, T>::value, "get_value_or: U must be convertible to T"); return static_cast<bool>(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(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<T>::value /*&& std::is_nothrow_swappable<T>::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<T>(*source); source.reset(); } } private: constexpr const T& get() const { return this->value_; } T& get() { return this->value_; } template <typename... Args> void construct(Args&&... args) { ::new (static_cast<void*>(&this->value_)) T(std::forward<Args>(args)...); has_value_ = true; } void destroy() noexcept { if (has_value_) { value_.~T(); has_value_ = false; } } template <typename U> void assign(U&& u) { if (has_value_) { value_ = std::forward<U>(u); } else { construct(std::forward<U>(u)); } } }; template <typename T> typename std::enable_if<std::is_nothrow_move_constructible<T>::value,void>::type swap(optional<T>& lhs, optional<T>& rhs) noexcept { lhs.swap(rhs); } template <class T1, class T2> constexpr bool operator==(const optional<T1>& lhs, const optional<T2>& rhs) noexcept { return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); } template <class T1, class T2> constexpr bool operator!=(const optional<T1>& lhs, const optional<T2>& rhs) noexcept { return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); } template <class T1, class T2> constexpr bool operator<(const optional<T1>& lhs, const optional<T2>& rhs) noexcept { return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); } template <class T1, class T2> constexpr bool operator>(const optional<T1>& lhs, const optional<T2>& rhs) noexcept { return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); } template <class T1, class T2> constexpr bool operator<=(const optional<T1>& lhs, const optional<T2>& rhs) noexcept { return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); } template <class T1, class T2> constexpr bool operator>=(const optional<T1>& lhs, const optional<T2>& rhs) noexcept { return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); } template <class T1, class T2> constexpr bool operator==(const optional<T1>& lhs, const T2& rhs) noexcept { return lhs ? *lhs == rhs : false; } template <class T1, class T2> constexpr bool operator==(const T1& lhs, const optional<T2>& rhs) noexcept { return rhs ? lhs == *rhs : false; } template <class T1, class T2> constexpr bool operator!=(const optional<T1>& lhs, const T2& rhs) noexcept { return lhs ? *lhs != rhs : true; } template <class T1, class T2> constexpr bool operator!=(const T1& lhs, const optional<T2>& rhs) noexcept { return rhs ? lhs != *rhs : true; } template <class T1, class T2> constexpr bool operator<(const optional<T1>& lhs, const T2& rhs) noexcept { return lhs ? *lhs < rhs : true; } template <class T1, class T2> constexpr bool operator<(const T1& lhs, const optional<T2>& rhs) noexcept { return rhs ? lhs < *rhs : false; } template <class T1, class T2> constexpr bool operator<=(const optional<T1>& lhs, const T2& rhs) noexcept { return lhs ? *lhs <= rhs : true; } template <class T1, class T2> constexpr bool operator<=(const T1& lhs, const optional<T2>& rhs) noexcept { return rhs ? lhs <= *rhs : false; } template <class T1, class T2> constexpr bool operator>(const optional<T1>& lhs, const T2& rhs) noexcept { return lhs ? *lhs > rhs : false; } template <class T1, class T2> constexpr bool operator>(const T1& lhs, const optional<T2>& rhs) noexcept { return rhs ? lhs > *rhs : true; } template <class T1, class T2> constexpr bool operator>=(const optional<T1>& lhs, const T2& rhs) noexcept { return lhs ? *lhs >= rhs : false; } template <class T1, class T2> constexpr bool operator>=(const T1& lhs, const optional<T2>& rhs) noexcept { return rhs ? lhs >= *rhs : true; } } // namespace detail } // namespace jsoncons #endif