aboutsummaryrefslogtreecommitdiff
path: root/include/jsoncons/detail/optional.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/jsoncons/detail/optional.hpp')
-rw-r--r--include/jsoncons/detail/optional.hpp483
1 files changed, 483 insertions, 0 deletions
diff --git a/include/jsoncons/detail/optional.hpp b/include/jsoncons/detail/optional.hpp
new file mode 100644
index 0000000..492cd76
--- /dev/null
+++ b/include/jsoncons/detail/optional.hpp
@@ -0,0 +1,483 @@
+// 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