/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XTL_OPTIONAL_HPP #define XTL_OPTIONAL_HPP #include #include #include #include #ifdef __CLING__ #include #endif #include "xoptional_meta.hpp" #include "xclosure.hpp" #include "xfunctional.hpp" #include "xmeta_utils.hpp" #include "xtl_config.hpp" #include "xtype_traits.hpp" namespace xtl { template auto optional(T&& t, B&& b) noexcept; /************************ * optional declaration * ************************/ /** * @class xoptional * @brief Optional value handler. * * The xoptional is an optional proxy. It holds a value (or a reference on a value) and a flag (or reference on a flag) * indicating whether the element should be considered missing. * * xoptional is different from std::optional * * - no `operator->()` that returns a pointer. * - no `operator*()` that returns a value. * * The only way to access the underlying value and flag is with the `value` and `value_or` methods. * * - no explicit convertion to bool. This may lead to confusion when the underlying value type is boolean too. * * @tparam CT Closure type for the value. * @tparam CB Closure type for the missing flag. A falsy flag means that the value is missing. * * \ref xoptional is used both as a value type (with CT and CB being value types) and reference type for containers * with CT and CB being reference types. In other words, it serves as a reference proxy. * */ template class xoptional { public: using self_type = xoptional; using value_closure = CT; using flag_closure = CB; using value_type = std::decay_t; using flag_type = std::decay_t; // Constructors inline xoptional() : m_value(), m_flag(false) { } template , std::decay_t>>, std::is_constructible, std::is_convertible >::value, bool > = true> inline constexpr xoptional(T&& rhs) : m_value(std::forward(rhs)), m_flag(true) { } template , std::decay_t>>, std::is_constructible, negation> >::value, bool > = false> inline explicit constexpr xoptional(T&& value) : m_value(std::forward(value)), m_flag(true) { } template , xoptional>>, std::is_constructible>>, std::is_constructible>>, conjunction< std::is_convertible>, CT>, std::is_convertible>, CB> >, negation> >::value, bool > = true> inline constexpr xoptional(const xoptional& rhs) : m_value(rhs.value()), m_flag(rhs.has_value()) { } template , xoptional>>, std::is_constructible>>, std::is_constructible>>, disjunction< negation>, CT>>, negation>, CB>> >, negation> >::value, bool > = false> inline explicit constexpr xoptional(const xoptional& rhs) : m_value(rhs.value()), m_flag(rhs.has_value()) { } template , xoptional>>, std::is_constructible::value, const std::decay_t&, std::decay_t&&>>, std::is_constructible::value, const std::decay_t&, std::decay_t&&>>, conjunction< std::is_convertible::value, const std::decay_t&, std::decay_t&&>, CT>, std::is_convertible::value, const std::decay_t&, std::decay_t&&>, CB> >, negation> >::value, bool > = true> inline constexpr xoptional(xoptional&& rhs) : m_value(std::move(rhs).value()), m_flag(std::move(rhs).has_value()) { } template , xoptional>>, std::is_constructible::value, const std::decay_t&, std::decay_t&&>>, std::is_constructible::value, const std::decay_t&, std::decay_t&&>>, disjunction< negation::value, const std::decay_t&, std::decay_t&&>, CT>>, negation::value, const std::decay_t&, std::decay_t&&>, CB>> >, negation> >::value, bool > = false> inline explicit constexpr xoptional(xoptional&& rhs) : m_value(std::move(rhs).value()), m_flag(std::move(rhs).has_value()) { } xoptional(value_type&&, flag_type&&); xoptional(std::add_lvalue_reference_t, std::add_lvalue_reference_t); xoptional(value_type&&, std::add_lvalue_reference_t); xoptional(std::add_lvalue_reference_t, flag_type&&); // Assignment template std::enable_if_t< conjunction< negation, std::decay_t>>, std::is_assignable, T> >::value, xoptional&> inline operator=(T&& rhs) { m_value = std::forward(rhs); m_flag = true; return *this; } template std::enable_if_t, xoptional>>, std::is_assignable, CTO>, negation>, negation> >::value, xoptional&> inline operator=(const xoptional& rhs) { m_value = rhs.value(); m_flag = rhs.has_value(); return *this; } template std::enable_if_t, xoptional>>, std::is_assignable, CTO>, negation>, negation> >::value, xoptional&> inline operator=(xoptional&& rhs) { m_value = std::move(rhs).value(); m_flag = std::move(rhs).has_value(); return *this; } // Operators template xoptional& operator+=(const xoptional&); template xoptional& operator-=(const xoptional&); template xoptional& operator*=(const xoptional&); template xoptional& operator/=(const xoptional&); template xoptional& operator%=(const xoptional&); template xoptional& operator&=(const xoptional&); template xoptional& operator|=(const xoptional&); template xoptional& operator^=(const xoptional&); template )> xoptional& operator+=(const T&); template )> xoptional& operator-=(const T&); template )> xoptional& operator*=(const T&); template )> xoptional& operator/=(const T&); template )> xoptional& operator%=(const T&); template )> xoptional& operator&=(const T&); template )> xoptional& operator|=(const T&); template )> xoptional& operator^=(const T&); // Access std::add_lvalue_reference_t value() & noexcept; std::add_lvalue_reference_t> value() const & noexcept; std::conditional_t::value, apply_cv_t&, value_type> value() && noexcept; std::conditional_t::value, const value_type&, value_type> value() const && noexcept; template value_type value_or(U&&) const & noexcept; template value_type value_or(U&&) const && noexcept; // Access std::add_lvalue_reference_t has_value() & noexcept; std::add_lvalue_reference_t> has_value() const & noexcept; std::conditional_t::value, apply_cv_t&, flag_type> has_value() && noexcept; std::conditional_t::value, const flag_type&, flag_type> has_value() const && noexcept; // Swap void swap(xoptional& other); // Comparison template bool equal(const xoptional& rhs) const noexcept; template )> bool equal(const CTO& rhs) const noexcept; xclosure_pointer operator&() &; xclosure_pointer operator&() const &; xclosure_pointer operator&() &&; private: template friend class xoptional; CT m_value; CB m_flag; }; // value template >> T&& value(T&& v) { return std::forward(v); } template decltype(auto) value(xtl::xoptional&& v) { return std::move(v).value(); } template decltype(auto) value(xtl::xoptional& v) { return v.value(); } template decltype(auto) value(const xtl::xoptional& v) { return v.value(); } // has_value template >> bool has_value(T&&) { return true; } template decltype(auto) has_value(xtl::xoptional&& v) { return std::move(v).has_value(); } template decltype(auto) has_value(xtl::xoptional& v) { return v.has_value(); } template decltype(auto) has_value(const xtl::xoptional& v) { return v.has_value(); } /*************************************** * optional and missing implementation * ***************************************/ /** * @brief Returns an \ref xoptional holding closure types on the specified parameters * * @tparam t the optional value * @tparam b the boolean flag */ template inline auto optional(T&& t, B&& b) noexcept { using optional_type = xoptional, closure_type_t>; return optional_type(std::forward(t), std::forward(b)); } /** * @brief Returns an \ref xoptional for a missig value */ template xoptional missing() noexcept { return xoptional(T(), false); } /**************************** * xoptional implementation * ****************************/ // Constructors template xoptional::xoptional(value_type&& value, flag_type&& flag) : m_value(std::move(value)), m_flag(std::move(flag)) { } template xoptional::xoptional(std::add_lvalue_reference_t value, std::add_lvalue_reference_t flag) : m_value(value), m_flag(flag) { } template xoptional::xoptional(value_type&& value, std::add_lvalue_reference_t flag) : m_value(std::move(value)), m_flag(flag) { } template xoptional::xoptional(std::add_lvalue_reference_t value, flag_type&& flag) : m_value(value), m_flag(std::move(flag)) { } // Operators template template auto xoptional::operator+=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value += rhs.m_value; } return *this; } template template auto xoptional::operator-=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value -= rhs.m_value; } return *this; } template template auto xoptional::operator*=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value *= rhs.m_value; } return *this; } template template auto xoptional::operator/=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value /= rhs.m_value; } return *this; } template template auto xoptional::operator%=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value %= rhs.m_value; } return *this; } template template auto xoptional::operator&=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value &= rhs.m_value; } return *this; } template template auto xoptional::operator|=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value |= rhs.m_value; } return *this; } template template auto xoptional::operator^=(const xoptional& rhs) -> xoptional& { m_flag = m_flag && rhs.m_flag; if (m_flag) { m_value ^= rhs.m_value; } return *this; } template template >> auto xoptional::operator+=(const T& rhs) -> xoptional& { if (m_flag) { m_value += rhs; } return *this; } template template >> auto xoptional::operator-=(const T& rhs) -> xoptional& { if (m_flag) { m_value -= rhs; } return *this; } template template >> auto xoptional::operator*=(const T& rhs) -> xoptional& { if (m_flag) { m_value *= rhs; } return *this; } template template >> auto xoptional::operator/=(const T& rhs) -> xoptional& { if (m_flag) { m_value /= rhs; } return *this; } template template >> auto xoptional::operator%=(const T& rhs) -> xoptional& { if (m_flag) { m_value %= rhs; } return *this; } template template >> auto xoptional::operator&=(const T& rhs) -> xoptional& { if (m_flag) { m_value &= rhs; } return *this; } template template >> auto xoptional::operator|=(const T& rhs) -> xoptional& { if (m_flag) { m_value |= rhs; } return *this; } template template >> auto xoptional::operator^=(const T& rhs) -> xoptional& { if (m_flag) { m_value ^= rhs; } return *this; } // Access template auto xoptional::value() & noexcept -> std::add_lvalue_reference_t { return m_value; } template auto xoptional::value() const & noexcept -> std::add_lvalue_reference_t> { return m_value; } template auto xoptional::value() && noexcept -> std::conditional_t::value, apply_cv_t&, value_type> { return m_value; } template auto xoptional::value() const && noexcept -> std::conditional_t::value, const value_type&, value_type> { return m_value; } template template auto xoptional::value_or(U&& default_value) const & noexcept -> value_type { return m_flag ? m_value : std::forward(default_value); } template template auto xoptional::value_or(U&& default_value) const && noexcept -> value_type { return m_flag ? m_value : std::forward(default_value); } // Access template auto xoptional::has_value() & noexcept -> std::add_lvalue_reference_t { return m_flag; } template auto xoptional::has_value() const & noexcept -> std::add_lvalue_reference_t> { return m_flag; } template auto xoptional::has_value() && noexcept -> std::conditional_t::value, apply_cv_t&, flag_type> { return m_flag; } template auto xoptional::has_value() const && noexcept -> std::conditional_t::value, const flag_type&, flag_type> { return m_flag; } // Swap template void xoptional::swap(xoptional& other) { std::swap(m_value, other.m_value); std::swap(m_flag, other.m_flag); } // Comparison template template auto xoptional::equal(const xoptional& rhs) const noexcept -> bool { return (!m_flag && !rhs.m_flag) || (m_value == rhs.m_value && (m_flag && rhs.m_flag)); } template template >> bool xoptional::equal(const CTO& rhs) const noexcept { return m_flag ? (m_value == rhs) : false; } template inline auto xoptional::operator&() & -> xclosure_pointer { return xclosure_pointer(*this); } template inline auto xoptional::operator&() const & -> xclosure_pointer { return xclosure_pointer(*this); } template inline auto xoptional::operator&() && -> xclosure_pointer { return xclosure_pointer(std::move(*this)); } // External operators template inline std::basic_ostream& operator<<(std::basic_ostream& out, const xoptional& v) { if (v.has_value()) { out << v.value(); } else { out << "N/A"; } return out; } #ifdef __CLING__ template nlohmann::json mime_bundle_repr(const xoptional& v) { auto bundle = nlohmann::json::object(); std::stringstream tmp; tmp << v; bundle["text/plain"] = tmp.str(); return bundle; } #endif template inline auto operator==(const xoptional& e1, const xoptional& e2) noexcept -> bool { return e1.equal(e2); } template )> inline bool operator==(const xoptional& e1, const T2& e2) noexcept { return e1.equal(e2); } template )> inline bool operator==(const T1& e1, const xoptional& e2) noexcept { return e2.equal(e1); } template inline auto operator+(const xoptional& e) noexcept -> xoptional> { return e; } template inline auto operator!=(const xoptional& e1, const xoptional& e2) noexcept -> bool { return !e1.equal(e2); } template )> inline bool operator!=(const xoptional& e1, const T2& e2) noexcept { return !e1.equal(e2); } template )> inline bool operator!=(const T1& e1, const xoptional& e2) noexcept { return !e2.equal(e1); } // Operations template inline auto operator-(const xoptional& e) noexcept -> xoptional> { using value_type = std::decay_t; return e.has_value() ? -e.value() : missing(); } template inline auto operator+(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() + e2.value() : missing(); } template )> inline auto operator+(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 + e2.value() : missing(); } template )> inline auto operator+(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() + e2 : missing(); } template inline auto operator-(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() - e2.value() : missing(); } template )> inline auto operator-(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 - e2.value() : missing(); } template )> inline auto operator-(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() - e2 : missing(); } template inline auto operator*(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() * e2.value() : missing(); } template )> inline auto operator*(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 * e2.value() : missing(); } template )> inline auto operator*(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() * e2 : missing(); } template inline auto operator/(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() / e2.value() : missing(); } template )> inline auto operator/(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 / e2.value() : missing(); } template )> inline auto operator/(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() / e2 : missing(); } template inline auto operator%(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() % e2.value() : missing(); } template )> inline auto operator%(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 % e2.value() : missing(); } template )> inline auto operator%(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() % e2 : missing(); } template inline auto operator~(const xoptional& e) noexcept -> xoptional> { using value_type = std::decay_t; return e.has_value() ? ~e.value() : missing(); } template inline auto operator&(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() & e2.value() : missing(); } template )> inline auto operator&(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 & e2.value() : missing(); } template )> inline auto operator&(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() & e2 : missing(); } template inline auto operator|(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() | e2.value() : missing(); } template )> inline auto operator|(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 | e2.value() : missing(); } template )> inline auto operator|(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() | e2 : missing(); } template inline auto operator^(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() ^ e2.value() : missing(); } template )> inline auto operator^(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 ^ e2.value() : missing(); } template )> inline auto operator^(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() ^ e2 : missing(); } template inline auto operator||(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() || e2.value() : missing(); } template )> inline auto operator||(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 || e2.value() : missing(); } template )> inline auto operator||(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() || e2 : missing(); } template inline auto operator&&(const xoptional& e1, const xoptional& e2) noexcept -> xoptional, std::decay_t>> { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() && e2.has_value() ? e1.value() && e2.value() : missing(); } template )> inline auto operator&&(const T1& e1, const xoptional& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e2.has_value() ? e1 && e2.value() : missing(); } template )> inline auto operator&&(const xoptional& e1, const T2& e2) noexcept -> common_optional_t { using value_type = std::common_type_t, std::decay_t>; return e1.has_value() ? e1.value() && e2 : missing(); } template inline auto operator!(const xoptional& e) noexcept -> xoptional { return e.has_value() ? !e.value() : missing(); } template inline auto operator<(const xoptional& e1, const xoptional& e2) noexcept -> xoptional { return e1.has_value() && e2.has_value() ? e1.value() < e2.value() : missing(); } template )> inline auto operator<(const T1& e1, const xoptional& e2) noexcept -> xoptional { return e2.has_value() ? e1 < e2.value() : missing(); } template )> inline auto operator<(const xoptional& e1, const T2& e2) noexcept -> xoptional { return e1.has_value() ? e1.value() < e2 : missing(); } template inline auto operator<=(const xoptional& e1, const xoptional& e2) noexcept -> xoptional { return e1.has_value() && e2.has_value() ? e1.value() <= e2.value() : missing(); } template )> inline auto operator<=(const T1& e1, const xoptional& e2) noexcept -> xoptional { return e2.has_value() ? e1 <= e2.value() : missing(); } template )> inline auto operator<=(const xoptional& e1, const T2& e2) noexcept -> xoptional { return e1.has_value() ? e1.value() <= e2 : missing(); } template inline auto operator>(const xoptional& e1, const xoptional& e2) noexcept -> xoptional { return e1.has_value() && e2.has_value() ? e1.value() > e2.value() : missing(); } template )> inline auto operator>(const T1& e1, const xoptional& e2) noexcept -> xoptional { return e2.has_value() ? e1 > e2.value() : missing(); } template )> inline auto operator>(const xoptional& e1, const T2& e2) noexcept -> xoptional { return e1.has_value() ? e1.value() > e2 : missing(); } template inline auto operator>=(const xoptional& e1, const xoptional& e2) noexcept -> xoptional { return e1.has_value() && e2.has_value() ? e1.value() >= e2.value() : missing(); } template )> inline auto operator>=(const T1& e1, const xoptional& e2) noexcept -> xoptional { return e2.has_value() ? e1 >= e2.value() : missing(); } template )> inline auto operator>=(const xoptional& e1, const T2& e2) noexcept -> xoptional { return e1.has_value() ? e1.value() >= e2 : missing(); } #define UNARY_OPTIONAL(NAME) \ template \ inline auto NAME(const xoptional& e) \ { \ using std::NAME; \ return e.has_value() ? NAME(e.value()) : missing>(); \ } #define UNARY_BOOL_OPTIONAL(NAME) \ template \ inline xoptional NAME(const xoptional& e) \ { \ using std::NAME; \ return e.has_value() ? bool(NAME(e.value())) : missing(); \ } #define BINARY_OPTIONAL_1(NAME) \ template )> \ inline auto NAME(const xoptional& e1, const T2& e2) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t>; \ return e1.has_value() ? NAME(e1.value(), e2) : missing(); \ } #define BINARY_OPTIONAL_2(NAME) \ template )> \ inline auto NAME(const T1& e1, const xoptional& e2) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t>; \ return e2.has_value() ? NAME(e1, e2.value()) : missing(); \ } #define BINARY_OPTIONAL_12(NAME) \ template \ inline auto NAME(const xoptional& e1, const xoptional& e2) \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t>; \ return e1.has_value() && e2.has_value() ? NAME(e1.value(), e2.value()) : missing(); \ } #define BINARY_OPTIONAL(NAME) \ BINARY_OPTIONAL_1(NAME) \ BINARY_OPTIONAL_2(NAME) \ BINARY_OPTIONAL_12(NAME) #define TERNARY_OPTIONAL_1(NAME) \ template , is_not_xoptional_nor_xmasked_value)> \ inline auto NAME(const xoptional& e1, const T2& e2, const T3& e3) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t, std::decay_t>; \ return e1.has_value() ? NAME(e1.value(), e2, e3) : missing(); \ } #define TERNARY_OPTIONAL_2(NAME) \ template , is_not_xoptional_nor_xmasked_value)> \ inline auto NAME(const T1& e1, const xoptional& e2, const T3& e3) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t, std::decay_t>; \ return e2.has_value() ? NAME(e1, e2.value(), e3) : missing(); \ } #define TERNARY_OPTIONAL_3(NAME) \ template , is_not_xoptional_nor_xmasked_value)> \ inline auto NAME(const T1& e1, const T2& e2, const xoptional& e3) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t, std::decay_t>; \ return e3.has_value() ? NAME(e1, e2, e3.value()) : missing(); \ } #define TERNARY_OPTIONAL_12(NAME) \ template )> \ inline auto NAME(const xoptional& e1, const xoptional& e2, const T3& e3) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t, std::decay_t>; \ return (e1.has_value() && e2.has_value()) ? NAME(e1.value(), e2.value(), e3) : missing(); \ } #define TERNARY_OPTIONAL_13(NAME) \ template )> \ inline auto NAME(const xoptional& e1, const T2& e2, const xoptional& e3) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t, std::decay_t>; \ return (e1.has_value() && e3.has_value()) ? NAME(e1.value(), e2, e3.value()) : missing(); \ } #define TERNARY_OPTIONAL_23(NAME) \ template )> \ inline auto NAME(const T1& e1, const xoptional& e2, const xoptional& e3) \ -> common_optional_t \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t, std::decay_t>; \ return (e2.has_value() && e3.has_value()) ? NAME(e1, e2.value(), e3.value()) : missing(); \ } #define TERNARY_OPTIONAL_123(NAME) \ template \ inline auto NAME(const xoptional& e1, const xoptional& e2, const xoptional& e3) \ { \ using std::NAME; \ using value_type = std::common_type_t, std::decay_t, std::decay_t>; \ return (e1.has_value() && e2.has_value() && e3.has_value()) ? NAME(e1.value(), e2.value(), e3.value()) : missing(); \ } #define TERNARY_OPTIONAL(NAME) \ TERNARY_OPTIONAL_1(NAME) \ TERNARY_OPTIONAL_2(NAME) \ TERNARY_OPTIONAL_3(NAME) \ TERNARY_OPTIONAL_12(NAME) \ TERNARY_OPTIONAL_13(NAME) \ TERNARY_OPTIONAL_23(NAME) \ TERNARY_OPTIONAL_123(NAME) UNARY_OPTIONAL(abs) UNARY_OPTIONAL(fabs) BINARY_OPTIONAL(fmod) BINARY_OPTIONAL(remainder) TERNARY_OPTIONAL(fma) BINARY_OPTIONAL(fmax) BINARY_OPTIONAL(fmin) BINARY_OPTIONAL(fdim) UNARY_OPTIONAL(exp) UNARY_OPTIONAL(exp2) UNARY_OPTIONAL(expm1) UNARY_OPTIONAL(log) UNARY_OPTIONAL(log10) UNARY_OPTIONAL(log2) UNARY_OPTIONAL(log1p) BINARY_OPTIONAL(pow) UNARY_OPTIONAL(sqrt) UNARY_OPTIONAL(cbrt) BINARY_OPTIONAL(hypot) UNARY_OPTIONAL(sin) UNARY_OPTIONAL(cos) UNARY_OPTIONAL(tan) UNARY_OPTIONAL(acos) UNARY_OPTIONAL(asin) UNARY_OPTIONAL(atan) BINARY_OPTIONAL(atan2) UNARY_OPTIONAL(sinh) UNARY_OPTIONAL(cosh) UNARY_OPTIONAL(tanh) UNARY_OPTIONAL(acosh) UNARY_OPTIONAL(asinh) UNARY_OPTIONAL(atanh) UNARY_OPTIONAL(erf) UNARY_OPTIONAL(erfc) UNARY_OPTIONAL(tgamma) UNARY_OPTIONAL(lgamma) UNARY_OPTIONAL(ceil) UNARY_OPTIONAL(floor) UNARY_OPTIONAL(trunc) UNARY_OPTIONAL(round) UNARY_OPTIONAL(nearbyint) UNARY_OPTIONAL(rint) UNARY_BOOL_OPTIONAL(isfinite) UNARY_BOOL_OPTIONAL(isinf) UNARY_BOOL_OPTIONAL(isnan) #undef TERNARY_OPTIONAL #undef TERNARY_OPTIONAL_123 #undef TERNARY_OPTIONAL_23 #undef TERNARY_OPTIONAL_13 #undef TERNARY_OPTIONAL_12 #undef TERNARY_OPTIONAL_3 #undef TERNARY_OPTIONAL_2 #undef TERNARY_OPTIONAL_1 #undef BINARY_OPTIONAL #undef BINARY_OPTIONAL_12 #undef BINARY_OPTIONAL_2 #undef BINARY_OPTIONAL_1 #undef UNARY_OPTIONAL /************************* * select implementation * *************************/ template )> inline common_optional_t select(const B& cond, const T1& v1, const T2& v2) noexcept { using bool_type = common_optional_t; using return_type = common_optional_t; bool_type opt_cond(cond); return opt_cond.has_value() ? opt_cond.value() ? return_type(v1) : return_type(v2) : missing(); } } #endif