/* ============================================================================== 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 constexpr auto isNothrowSwappable = noexcept (swap (std::declval(), std::declval())); } // 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 class Optional { template struct NotConstructibleFromSimilarType { static constexpr auto value = ! std::is_constructible&>::value && ! std::is_constructible&>::value && ! std::is_constructible&&>::value && ! std::is_constructible&&>::value && ! std::is_convertible&, T>::value && ! std::is_convertible&, T>::value && ! std::is_convertible&&, T>::value && ! std::is_convertible&&, T>::value; }; template using OptionalCopyConstructorEnabled = std::enable_if_t::value && NotConstructibleFromSimilarType::value>; template using OptionalMoveConstructorEnabled = std::enable_if_t::value && NotConstructibleFromSimilarType::value>; template static auto notAssignableFromSimilarType = NotConstructibleFromSimilarType::value && ! std::is_assignable&>::value && ! std::is_assignable&>::value && ! std::is_assignable&&>::value && ! std::is_assignable&&>::value; template using OptionalCopyAssignmentEnabled = std::enable_if_t::value && std::is_assignable::value && NotConstructibleFromSimilarType::value>; template using OptionalMoveAssignmentEnabled = std::enable_if_t::value && std::is_nothrow_assignable::value && NotConstructibleFromSimilarType::value>; public: Optional() : placeholder() {} Optional (Nullopt) noexcept : placeholder() {} template ::value && ! std::is_same, Optional>::value>> Optional (U&& value) noexcept (noexcept (Value (std::forward (value)))) : storage (std::forward (value)), valid (true) { } Optional (Optional&& other) noexcept (noexcept (std::declval().constructFrom (other))) : placeholder() { constructFrom (other); } Optional (const Optional& other) : placeholder(), valid (other.valid) { if (valid) new (&storage) Value (*other); } template > Optional (Optional&& other) noexcept (noexcept (std::declval().constructFrom (other))) : placeholder() { constructFrom (other); } template > Optional (const Optional& other) : placeholder(), valid (other.hasValue()) { if (valid) new (&storage) Value (*other); } Optional& operator= (Nullopt) noexcept { reset(); return *this; } template ::value && std::is_nothrow_move_assignable::value>> Optional& operator= (Optional&& other) noexcept (noexcept (std::declval().assign (std::declval()))) { assign (other); return *this; } template , Optional>::value && std::is_constructible::value && std::is_assignable::value && (! std::is_scalar::value || ! std::is_same, Value>::value)>> Optional& operator= (U&& value) { if (valid) **this = std::forward (value); else new (&storage) Value (std::forward (value)); valid = true; return *this; } /** Maintains the strong exception safety guarantee. */ Optional& operator= (const Optional& other) { auto copy = other; assign (copy); return *this; } template > Optional& operator= (Optional&& other) noexcept (noexcept (std::declval().assign (other))) { assign (other); return *this; } /** Maintains the strong exception safety guarantee. */ template > Optional& operator= (const Optional& 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 (&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 Value orFallback (U&& fallback) const { return *this ? **this : std::forward (fallback); } template Value& emplace (Args&&... args) { reset(); new (&storage) Value (std::forward (args)...); valid = true; return **this; } void swap (Optional& other) noexcept (std::is_nothrow_move_constructible::value && detail::adlSwap::isNothrowSwappable) { if (hasValue() && other.hasValue()) { using std::swap; swap (**this, *other); } else if (hasValue() || other.hasValue()) { (hasValue() ? other : *this).constructFrom (hasValue() ? *this : other); } } private: template void constructFrom (Optional& other) noexcept (noexcept (Value (std::move (*other)))) { if (! other.hasValue()) return; new (&storage) Value (std::move (*other)); valid = true; other.reset(); } template void assign (Optional& other) noexcept (noexcept (std::declval() = std::move (*other)) && noexcept (std::declval().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 Optional> makeOptional (Value&& v) { return std::forward (v); } template bool operator== (const Optional& lhs, const Optional& rhs) { if (lhs.hasValue() != rhs.hasValue()) return false; if (! lhs.hasValue()) return true; return *lhs == *rhs; } template bool operator!= (const Optional& lhs, const Optional& rhs) { if (lhs.hasValue() != rhs.hasValue()) return true; if (! lhs.hasValue()) return false; return *lhs != *rhs; } template bool operator< (const Optional& lhs, const Optional& rhs) { if (! rhs.hasValue()) return false; if (! lhs.hasValue()) return true; return *lhs < *rhs; } template bool operator<= (const Optional& lhs, const Optional& rhs) { if (! lhs.hasValue()) return true; if (! rhs.hasValue()) return false; return *lhs <= *rhs; } template bool operator> (const Optional& lhs, const Optional& rhs) { if (! lhs.hasValue()) return false; if (! rhs.hasValue()) return true; return *lhs > *rhs; } template bool operator>= (const Optional& lhs, const Optional& rhs) { if (! rhs.hasValue()) return true; if (! lhs.hasValue()) return false; return *lhs >= *rhs; } template bool operator== (const Optional& opt, Nullopt) noexcept { return ! opt.hasValue(); } template bool operator== (Nullopt, const Optional& opt) noexcept { return ! opt.hasValue(); } template bool operator!= (const Optional& opt, Nullopt) noexcept { return opt.hasValue(); } template bool operator!= (Nullopt, const Optional& opt) noexcept { return opt.hasValue(); } template bool operator< (const Optional&, Nullopt) noexcept { return false; } template bool operator< (Nullopt, const Optional& opt) noexcept { return opt.hasValue(); } template bool operator<= (const Optional& opt, Nullopt) noexcept { return ! opt.hasValue(); } template bool operator<= (Nullopt, const Optional&) noexcept { return true; } template bool operator> (const Optional& opt, Nullopt) noexcept { return opt.hasValue(); } template bool operator> (Nullopt, const Optional&) noexcept { return false; } template bool operator>= (const Optional&, Nullopt) noexcept { return true; } template bool operator>= (Nullopt, const Optional& opt) noexcept { return ! opt.hasValue(); } template bool operator== (const Optional& opt, const U& value) { return opt.hasValue() ? *opt == value : false; } template bool operator== (const T& value, const Optional& opt) { return opt.hasValue() ? value == *opt : false; } template bool operator!= (const Optional& opt, const U& value) { return opt.hasValue() ? *opt != value : true; } template bool operator!= (const T& value, const Optional& opt) { return opt.hasValue() ? value != *opt : true; } template bool operator< (const Optional& opt, const U& value) { return opt.hasValue() ? *opt < value : true; } template bool operator< (const T& value, const Optional& opt) { return opt.hasValue() ? value < *opt : false; } template bool operator<= (const Optional& opt, const U& value) { return opt.hasValue() ? *opt <= value : true; } template bool operator<= (const T& value, const Optional& opt) { return opt.hasValue() ? value <= *opt : false; } template bool operator> (const Optional& opt, const U& value) { return opt.hasValue() ? *opt > value : false; } template bool operator> (const T& value, const Optional& opt) { return opt.hasValue() ? value > *opt : true; } template bool operator>= (const Optional& opt, const U& value) { return opt.hasValue() ? *opt >= value : false; } template bool operator>= (const T& value, const Optional& opt) { return opt.hasValue() ? value >= *opt : true; } } // namespace juce