|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- namespace detail
- {
- namespace adlSwap
- {
- using std::swap;
-
- template <typename T>
- constexpr auto isNothrowSwappable = noexcept (swap (std::declval<T&>(), std::declval<T&>()));
- } // namespace adlSwap
- } // namespace detail
-
- /** A type representing the null state of an Optional.
- Similar to std::nullopt_t.
- */
- struct Nullopt
- {
- explicit constexpr Nullopt (int) {}
- };
-
- /** An object that can be used when constructing and comparing Optional instances.
- Similar to std::nullopt.
- */
- constexpr Nullopt nullopt { 0 };
-
- // Without this, our tests can emit "unreachable code" warnings during
- // link time code generation.
- JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4702)
-
- /**
- A simple optional type.
-
- Has similar (not necessarily identical!) semantics to std::optional.
-
- This is intended to stand-in for std::optional while JUCE's minimum
- supported language standard is lower than C++17. When the minimum language
- standard moves to C++17, this class will probably be deprecated, in much
- the same way that juce::ScopedPointer was deprecated in favour of
- std::unique_ptr after C++11.
-
- This isn't really intended to be used by JUCE clients. Instead, it's to be
- used internally in JUCE code, with an API close-enough to std::optional
- that the types can be swapped with fairly minor disruption at some point in
- the future, but *without breaking any public APIs*.
-
- @tags{Core}
- */
- template <typename Value>
- class Optional
- {
- template <typename T, typename U>
- struct NotConstructibleFromSimilarType
- {
- static constexpr auto value = ! std::is_constructible<T, Optional<U>&>::value
- && ! std::is_constructible<T, const Optional<U>&>::value
- && ! std::is_constructible<T, Optional<U>&&>::value
- && ! std::is_constructible<T, const Optional<U>&&>::value
- && ! std::is_convertible<Optional<U>&, T>::value
- && ! std::is_convertible<const Optional<U>&, T>::value
- && ! std::is_convertible<Optional<U>&&, T>::value
- && ! std::is_convertible<const Optional<U>&&, T>::value;
- };
-
- template <typename T, typename U>
- using OptionalCopyConstructorEnabled = std::enable_if_t<std::is_constructible<T, const U&>::value && NotConstructibleFromSimilarType<T, U>::value>;
-
- template <typename T, typename U>
- using OptionalMoveConstructorEnabled = std::enable_if_t<std::is_constructible<T, U&&>::value && NotConstructibleFromSimilarType<T, U>::value>;
-
- template <typename T, typename U>
- static auto notAssignableFromSimilarType = NotConstructibleFromSimilarType<T, U>::value
- && ! std::is_assignable<T&, Optional<U>&>::value
- && ! std::is_assignable<T&, const Optional<U>&>::value
- && ! std::is_assignable<T&, Optional<U>&&>::value
- && ! std::is_assignable<T&, const Optional<U>&&>::value;
-
- template <typename T, typename U>
- using OptionalCopyAssignmentEnabled = std::enable_if_t<std::is_constructible<T, const U&>::value
- && std::is_assignable<T&, const U&>::value
- && NotConstructibleFromSimilarType<T, U>::value>;
-
- template <typename T, typename U>
- using OptionalMoveAssignmentEnabled = std::enable_if_t<std::is_constructible<T, U>::value
- && std::is_nothrow_assignable<T&, U>::value
- && NotConstructibleFromSimilarType<T, U>::value>;
-
- public:
- Optional() : placeholder() {}
-
- Optional (Nullopt) noexcept : placeholder() {}
-
- template <typename U = Value,
- typename = std::enable_if_t<std::is_constructible<Value, U&&>::value
- && ! std::is_same<std::decay_t<U>, Optional>::value>>
- Optional (U&& value) noexcept (noexcept (Value (std::forward<U> (value))))
- : storage (std::forward<U> (value)), valid (true)
- {
- }
-
- Optional (Optional&& other) noexcept (noexcept (std::declval<Optional>().constructFrom (other)))
- : placeholder()
- {
- constructFrom (other);
- }
-
- Optional (const Optional& other)
- : placeholder(), valid (other.valid)
- {
- if (valid)
- new (&storage) Value (*other);
- }
-
- template <typename Other, typename = OptionalMoveConstructorEnabled<Value, Other>>
- Optional (Optional<Other>&& other) noexcept (noexcept (std::declval<Optional>().constructFrom (other)))
- : placeholder()
- {
- constructFrom (other);
- }
-
- template <typename Other, typename = OptionalCopyConstructorEnabled<Value, Other>>
- Optional (const Optional<Other>& other)
- : placeholder(), valid (other.hasValue())
- {
- if (valid)
- new (&storage) Value (*other);
- }
-
- Optional& operator= (Nullopt) noexcept
- {
- reset();
- return *this;
- }
-
- template <typename U = Value,
- typename = std::enable_if_t<std::is_nothrow_move_constructible<U>::value
- && std::is_nothrow_move_assignable<U>::value>>
- Optional& operator= (Optional&& other) noexcept (noexcept (std::declval<Optional>().assign (std::declval<Optional&>())))
- {
- assign (other);
- return *this;
- }
-
- template <typename U = Value,
- typename = std::enable_if_t<! std::is_same<std::decay_t<U>, Optional>::value
- && std::is_constructible<Value, U>::value
- && std::is_assignable<Value&, U>::value
- && (! std::is_scalar<Value>::value || ! std::is_same<std::decay_t<U>, Value>::value)>>
- Optional& operator= (U&& value)
- {
- if (valid)
- **this = std::forward<U> (value);
- else
- new (&storage) Value (std::forward<U> (value));
-
- valid = true;
- return *this;
- }
-
- /** Maintains the strong exception safety guarantee. */
- Optional& operator= (const Optional& other)
- {
- auto copy = other;
- assign (copy);
- return *this;
- }
-
- template <typename Other, typename = OptionalMoveAssignmentEnabled<Value, Other>>
- Optional& operator= (Optional<Other>&& other) noexcept (noexcept (std::declval<Optional>().assign (other)))
- {
- assign (other);
- return *this;
- }
-
- /** Maintains the strong exception safety guarantee. */
- template <typename Other, typename = OptionalCopyAssignmentEnabled<Value, Other>>
- Optional& operator= (const Optional<Other>& other)
- {
- auto copy = other;
- assign (copy);
- return *this;
- }
-
- ~Optional() noexcept
- {
- reset();
- }
-
- Value* operator->() noexcept { return reinterpret_cast< Value*> (&storage); }
- const Value* operator->() const noexcept { return reinterpret_cast<const Value*> (&storage); }
-
- Value& operator*() noexcept { return *operator->(); }
- const Value& operator*() const noexcept { return *operator->(); }
-
- explicit operator bool() const noexcept { return valid; }
- bool hasValue() const noexcept { return valid; }
-
- void reset()
- {
- if (std::exchange (valid, false))
- operator*().~Value();
- }
-
- /** Like std::optional::value_or */
- template <typename U>
- Value orFallback (U&& fallback) const { return *this ? **this : std::forward<U> (fallback); }
-
- template <typename... Args>
- Value& emplace (Args&&... args)
- {
- reset();
- new (&storage) Value (std::forward<Args> (args)...);
- valid = true;
- return **this;
- }
-
- void swap (Optional& other) noexcept (std::is_nothrow_move_constructible<Value>::value
- && detail::adlSwap::isNothrowSwappable<Value>)
- {
- if (hasValue() && other.hasValue())
- {
- using std::swap;
- swap (**this, *other);
- }
- else if (hasValue() || other.hasValue())
- {
- (hasValue() ? other : *this).constructFrom (hasValue() ? *this : other);
- }
- }
-
- private:
- template <typename Other>
- void constructFrom (Optional<Other>& other) noexcept (noexcept (Value (std::move (*other))))
- {
- if (! other.hasValue())
- return;
-
- new (&storage) Value (std::move (*other));
- valid = true;
- other.reset();
- }
-
- template <typename Other>
- void assign (Optional<Other>& other) noexcept (noexcept (std::declval<Value&>() = std::move (*other)) && noexcept (std::declval<Optional>().constructFrom (other)))
- {
- if (valid)
- {
- if (other.hasValue())
- {
- **this = std::move (*other);
- other.reset();
- }
- else
- {
- reset();
- }
- }
- else
- {
- constructFrom (other);
- }
- }
-
- union
- {
- char placeholder;
- Value storage;
- };
- bool valid = false;
- };
-
- JUCE_END_IGNORE_WARNINGS_MSVC
-
- template <typename Value>
- Optional<std::decay_t<Value>> makeOptional (Value&& v)
- {
- return std::forward<Value> (v);
- }
-
- template <class T, class U>
- bool operator== (const Optional<T>& lhs, const Optional<U>& rhs)
- {
- if (lhs.hasValue() != rhs.hasValue()) return false;
- if (! lhs.hasValue()) return true;
- return *lhs == *rhs;
- }
-
- template <class T, class U>
- bool operator!= (const Optional<T>& lhs, const Optional<U>& rhs)
- {
- if (lhs.hasValue() != rhs.hasValue()) return true;
- if (! lhs.hasValue()) return false;
- return *lhs != *rhs;
- }
-
- template <class T, class U>
- bool operator< (const Optional<T>& lhs, const Optional<U>& rhs)
- {
- if (! rhs.hasValue()) return false;
- if (! lhs.hasValue()) return true;
- return *lhs < *rhs;
- }
-
- template <class T, class U>
- bool operator<= (const Optional<T>& lhs, const Optional<U>& rhs)
- {
- if (! lhs.hasValue()) return true;
- if (! rhs.hasValue()) return false;
- return *lhs <= *rhs;
- }
-
- template <class T, class U>
- bool operator> (const Optional<T>& lhs, const Optional<U>& rhs)
- {
- if (! lhs.hasValue()) return false;
- if (! rhs.hasValue()) return true;
- return *lhs > *rhs;
- }
-
- template <class T, class U>
- bool operator>= (const Optional<T>& lhs, const Optional<U>& rhs)
- {
- if (! rhs.hasValue()) return true;
- if (! lhs.hasValue()) return false;
- return *lhs >= *rhs;
- }
-
- template <class T>
- bool operator== (const Optional<T>& opt, Nullopt) noexcept { return ! opt.hasValue(); }
- template <class T>
- bool operator== (Nullopt, const Optional<T>& opt) noexcept { return ! opt.hasValue(); }
- template <class T>
- bool operator!= (const Optional<T>& opt, Nullopt) noexcept { return opt.hasValue(); }
- template <class T>
- bool operator!= (Nullopt, const Optional<T>& opt) noexcept { return opt.hasValue(); }
- template <class T>
- bool operator< (const Optional<T>&, Nullopt) noexcept { return false; }
- template <class T>
- bool operator< (Nullopt, const Optional<T>& opt) noexcept { return opt.hasValue(); }
- template <class T>
- bool operator<= (const Optional<T>& opt, Nullopt) noexcept { return ! opt.hasValue(); }
- template <class T>
- bool operator<= (Nullopt, const Optional<T>&) noexcept { return true; }
- template <class T>
- bool operator> (const Optional<T>& opt, Nullopt) noexcept { return opt.hasValue(); }
- template <class T>
- bool operator> (Nullopt, const Optional<T>&) noexcept { return false; }
- template <class T>
- bool operator>= (const Optional<T>&, Nullopt) noexcept { return true; }
- template <class T>
- bool operator>= (Nullopt, const Optional<T>& opt) noexcept { return ! opt.hasValue(); }
-
- template <class T, class U>
- bool operator== (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt == value : false; }
- template <class T, class U>
- bool operator== (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value == *opt : false; }
- template <class T, class U>
- bool operator!= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt != value : true; }
- template <class T, class U>
- bool operator!= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value != *opt : true; }
- template <class T, class U>
- bool operator< (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt < value : true; }
- template <class T, class U>
- bool operator< (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value < *opt : false; }
- template <class T, class U>
- bool operator<= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt <= value : true; }
- template <class T, class U>
- bool operator<= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value <= *opt : false; }
- template <class T, class U>
- bool operator> (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt > value : false; }
- template <class T, class U>
- bool operator> (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value > *opt : true; }
- template <class T, class U>
- bool operator>= (const Optional<T>& opt, const U& value) { return opt.hasValue() ? *opt >= value : false; }
- template <class T, class U>
- bool operator>= (const T& value, const Optional<U>& opt) { return opt.hasValue() ? value >= *opt : true; }
-
- } // namespace juce
|