// Copyright (C) 2011 - 2012 Andrzej Krzemienski. // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // The idea and interface is based on Boost.Optional library // authored by Fernando Luis Cacciola Carballal # ifndef ___OPTIONAL_HPP___ # define ___OPTIONAL_HPP___ # include # include # include # include # include # include # include # define REQUIRES(...) typename enable_if<__VA_ARGS__::value, bool>::type = false # if defined __clang__ # define OPTIONAL_HAS_USING 1 # if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9) # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 # else # define OPTIONAL_HAS_THIS_RVALUE_REFS 0 # endif # elif defined __GNUC__ # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) # define OPTIONAL_HAS_USING 1 # else # define OPTIONAL_HAS_USING 0 # endif # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 8 || ((__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1)))) # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 # else # define OPTIONAL_HAS_THIS_RVALUE_REFS 0 # endif # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) # define OPTIONAL_GCC45_COMPATIBILITY 0 # else # define OPTIONAL_GCC45_COMPATIBILITY 1 # endif # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)) # define OPTIONAL_GCC44_COMPATIBILITY 0 # else # define OPTIONAL_GCC44_COMPATIBILITY 1 # endif # else # define OPTIONAL_HAS_THIS_RVALUE_REFS 0 # define OPTIONAL_HAS_USING 0 # define OPTIONAL_GCC45_COMPATIBILITY 0 # define OPTIONAL_GCC44_COMPATIBILITY 0 # endif namespace std{ # if (defined __GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))) // leave it; our metafunctions are already defined. # elif (defined __clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3) && (__clang_minor__ >= 3)) // leave it; our metafunctions are already defined. # else # if OPTIONAL_HAS_USING // the only bit GCC 4.7 and clang 3.2 don't have template using is_trivially_destructible = typename std::has_trivial_destructor; # endif # if (defined __GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))) // leave it; remaining metafunctions are already defined. # elif defined __clang__ // leave it; remaining metafunctions are already defined. # else #if !OPTIONAL_GCC45_COMPATIBILITY // workaround for missing traits in GCC and CLANG template struct is_nothrow_move_constructible { constexpr static bool value = std::is_nothrow_constructible::value; }; template struct is_assignable { template static constexpr bool has_assign(...) { return false; } template () = std::declval()) > static constexpr bool has_assign(bool) { return true; } constexpr static bool value = has_assign(true); }; template struct is_nothrow_move_assignable { template struct has_nothrow_move_assign { constexpr static bool value = false; }; template struct has_nothrow_move_assign { constexpr static bool value = noexcept( std::declval() = std::declval() ); }; constexpr static bool value = has_nothrow_move_assign::value>::value; }; // end workaround #endif # endif // not as good as GCC 4.7 # endif // not as good as GCC 4.8 // 20.5.4, optional for object types template class optional; // 20.5.5, optional for lvalue reference types template class optional; // workaround: std utility functions aren't constexpr yet template inline constexpr T&& constexpr_forward(typename std::remove_reference::type& t) noexcept { return static_cast(t); } template inline constexpr T&& constexpr_forward(typename std::remove_reference::type&& t) noexcept { static_assert(!std::is_lvalue_reference::value, "!!"); return static_cast(t); } template inline constexpr typename std::remove_reference::type&& constexpr_move(T&& t) noexcept { return static_cast::type&&>(t); } #if defined NDEBUG # define ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) #else # define ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : (fail(#CHECK, __FILE__, __LINE__), (EXPR))) inline void fail(const char* expr, const char* file, unsigned line) { # if defined(EMSCRIPTEN) && EMSCRIPTEN __assert_fail(expr, file, line, ""); # elif defined __clang__ || defined __GNU_LIBRARY__ __assert(expr, file, line); # elif defined __GNUC__ _assert(expr, file, line); # else # error UNSUPPORTED COMPILER # endif } #endif #if !OPTIONAL_GCC45_COMPATIBILITY template struct has_overloaded_addressof { template static constexpr bool has_overload(...) { return false; } template ().operator&()) > static constexpr bool has_overload(bool) { return true; } constexpr static bool value = has_overload(true); }; template )> constexpr T* static_addressof(T& ref) { return &ref; } template )> T* static_addressof(T& ref) { return std::addressof(ref); } #else template T* addressof(T& ref) { return reinterpret_cast(&const_cast(reinterpret_cast(ref))); } template T* static_addressof(T& ref) { return addressof(ref); } #endif template struct is_not_optional { #if !OPTIONAL_GCC45_COMPATIBILITY constexpr static bool value = true; #else static const bool value = true; #endif }; template struct is_not_optional> { #if !OPTIONAL_GCC45_COMPATIBILITY constexpr static bool value = false; #else static const bool value = false; #endif }; #if !OPTIONAL_GCC45_COMPATIBILITY constexpr struct trivial_init_t{} trivial_init{}; #else const struct trivial_init_t{} trivial_init{}; #endif // 20.5.6, In-place construction #if !OPTIONAL_GCC45_COMPATIBILITY constexpr struct in_place_t{} in_place{}; #else const struct in_place_t{} in_place{}; #endif // 20.5.7, Disengaged state indicator struct nullopt_t { struct init{}; constexpr nullopt_t(init){}; }; #if !OPTIONAL_GCC45_COMPATIBILITY constexpr nullopt_t nullopt{nullopt_t::init{}}; #else const nullopt_t nullopt{nullopt_t::init{}}; #endif // 20.5.8, class bad_optional_access class bad_optional_access : public logic_error { public: explicit bad_optional_access(const string& what_arg) : logic_error{what_arg} {} explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {} }; #if !OPTIONAL_GCC45_COMPATIBILITY template union storage_t { unsigned char dummy_; T value_; constexpr storage_t( trivial_init_t ) noexcept : dummy_() {}; template constexpr storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} ~storage_t(){} }; template union constexpr_storage_t { unsigned char dummy_; T value_; constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {}; template constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} ~constexpr_storage_t() = default; }; #else template struct storage_t { unsigned char storage_[sizeof(T)]; T& value_() { return *reinterpret_cast(storage_); } const T& value_() const { return *reinterpret_cast(storage_); } storage_t( trivial_init_t ): storage_() {} template storage_t( Args&&... args ) { new(storage_) T(forward(args)...); } ~storage_t(){} }; #endif #if !OPTIONAL_GCC45_COMPATIBILITY constexpr struct only_set_initialized_t{} only_set_initialized{}; #else const struct only_set_initialized_t{} only_set_initialized{}; #endif template struct optional_base { bool init_; storage_t storage_; constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {}; constexpr explicit optional_base(only_set_initialized_t, bool init) noexcept : init_(init), storage_(trivial_init) {}; explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {} explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} template explicit optional_base(in_place_t, Args&&... args) : init_(true), storage_(constexpr_forward(args)...) {} template >) #endif > explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) : init_(true), storage_(il, std::forward(args)...) {} ~optional_base() { #if !OPTIONAL_GCC45_COMPATIBILITY if (init_) storage_.value_.T::~T(); #else if (init_) storage_.value_().T::~T(); #endif } }; #if !OPTIONAL_GCC45_COMPATIBILITY template struct constexpr_optional_base { bool init_; constexpr_storage_t storage_; constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {}; constexpr explicit constexpr_optional_base(only_set_initialized_t, bool init) noexcept : init_(init), storage_(trivial_init) {}; explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {} explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} template explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) : init_(true), storage_(constexpr_forward(args)...) {} template >)> explicit constexpr_optional_base(in_place_t, std::initializer_list il, Args&&... args) : init_(true), storage_(il, std::forward(args)...) {} ~constexpr_optional_base() = default; }; #endif # if OPTIONAL_HAS_USING template using OptionalBase = typename std::conditional< std::is_trivially_destructible::value, constexpr_optional_base, optional_base >::type; # else # define OptionalBase optional_base # endif template class optional : private OptionalBase { static_assert( !std::is_same::type, nullopt_t>::value, "bad T" ); static_assert( !std::is_same::type, in_place_t>::value, "bad T" ); constexpr bool initialized() const noexcept { return OptionalBase::init_; } T* dataptr() { #if !OPTIONAL_GCC45_COMPATIBILITY return std::addressof(OptionalBase::storage_.value_); #else return std::addressof(OptionalBase::storage_.value_()); #endif } constexpr const T* dataptr() const { #if !OPTIONAL_GCC45_COMPATIBILITY return static_addressof(OptionalBase::storage_.value_); #else return static_addressof(OptionalBase::storage_.value_()); #endif } # if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 constexpr const T& contained_val() const& { return OptionalBase::storage_.value_; } T& contained_val() & { return OptionalBase::storage_.value_; } T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } # else constexpr const T& contained_val() const { #if !OPTIONAL_GCC45_COMPATIBILITY return OptionalBase::storage_.value_; #else return OptionalBase::storage_.value_(); #endif } T& contained_val() { #if !OPTIONAL_GCC45_COMPATIBILITY return OptionalBase::storage_.value_; #else return OptionalBase::storage_.value_(); #endif } # endif void clear() noexcept { if (initialized()) dataptr()->T::~T(); OptionalBase::init_ = false; } template void initialize(Args&&... args) #if !OPTIONAL_GCC45_COMPATIBILITY noexcept(noexcept(T(std::forward(args)...))) #endif { assert(!OptionalBase::init_); new (dataptr()) T(std::forward(args)...); OptionalBase::init_ = true; } template void initialize(std::initializer_list il, Args&&... args) #if !OPTIONAL_GCC45_COMPATIBILITY noexcept(noexcept(T(il, std::forward(args)...))) #endif { assert(!OptionalBase::init_); new (dataptr()) T(il, std::forward(args)...); OptionalBase::init_ = true; } public: typedef T value_type; // 20.5.5.1, constructors constexpr optional() noexcept : OptionalBase() {}; constexpr optional(nullopt_t) noexcept : OptionalBase() {}; optional(const optional& rhs) : OptionalBase(only_set_initialized, rhs.initialized()) { if (rhs.initialized()) new (dataptr()) T(*rhs); } optional(optional&& rhs) #if !OPTIONAL_GCC45_COMPATIBILITY noexcept(std::is_nothrow_move_constructible::value) #endif : OptionalBase(only_set_initialized, rhs.initialized()) { if (rhs.initialized()) new (dataptr()) T(std::move(*rhs)); } constexpr optional(const T& v) : OptionalBase(v) {} constexpr optional(T&& v) : OptionalBase(constexpr_move(v)) {} template constexpr explicit optional(in_place_t, Args&&... args) : OptionalBase(in_place_t{}, constexpr_forward(args)...) {} template >) #endif > explicit optional(in_place_t, std::initializer_list il, Args&&... args) : OptionalBase(in_place_t{}, il, constexpr_forward(args)...) {} // 20.5.4.2 Destructor ~optional() = default; // 20.5.4.3, assignment optional& operator=(nullopt_t) noexcept { clear(); return *this; } optional& operator=(const optional& rhs) { if (initialized() == true && rhs.initialized() == false) clear(); else if (initialized() == false && rhs.initialized() == true) initialize(*rhs); else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs; return *this; } optional& operator=(optional&& rhs) #if !OPTIONAL_GCC45_COMPATIBILITY noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) #endif { if (initialized() == true && rhs.initialized() == false) clear(); else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs)); else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs); return *this; } template auto operator=(U&& v) -> typename enable_if < is_same::type, T>::value, optional& >::type { if (initialized()) { contained_val() = std::forward(v); } else { initialize(std::forward(v)); } return *this; } template void emplace(Args&&... args) { clear(); initialize(std::forward(args)...); } template void emplace(initializer_list il, Args&&... args) { clear(); initialize(il, std::forward(args)...); } // 20.5.4.4 Swap void swap(optional& rhs) #if !OPTIONAL_GCC45_COMPATIBILITY noexcept(is_nothrow_move_constructible::value && noexcept(swap(declval(), declval()))) #endif { if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); } else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); } else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); } } // 20.5.4.5 Observers constexpr T const* operator ->() const { return ASSERTED_EXPRESSION(initialized(), dataptr()); } T* operator ->() { assert (initialized()); return dataptr(); } constexpr T const& operator *() const { return ASSERTED_EXPRESSION(initialized(), contained_val()); } T& operator *() { assert (initialized()); return contained_val(); } constexpr T const& value() const { return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); } T& value() { return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); } #if !OPTIONAL_GCC44_COMPATIBILITY constexpr explicit #endif operator bool() const noexcept { return initialized(); } # if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 template constexpr T value_or(V&& v) const& { return *this ? **this : static_cast(constexpr_forward(v)); } template T value_or(V&& v) && { return *this ? std::move(const_cast&>(*this).contained_val()) : static_cast(constexpr_forward(v)); } # else template constexpr T value_or(V&& v) const { return *this ? **this : static_cast(constexpr_forward(v)); } # endif }; template class optional { static_assert( !std::is_same::value, "bad T" ); static_assert( !std::is_same::value, "bad T" ); T* ref; public: // 20.5.5.1, construction/destruction constexpr optional() noexcept : ref(nullptr) {} constexpr optional(nullopt_t) noexcept : ref(nullptr) {} constexpr optional(T& v) noexcept : ref(static_addressof(v)) {} optional(T&&) = delete; constexpr optional(const optional& rhs) noexcept : ref(rhs.ref) {} explicit constexpr optional(in_place_t, T& v) noexcept : ref(static_addressof(v)) {} explicit optional(in_place_t, T&&) = delete; ~optional() = default; // 20.5.5.2, mutation optional& operator=(nullopt_t) noexcept { ref = nullptr; return *this; } // optional& operator=(const optional& rhs) noexcept { // ref = rhs.ref; // return *this; // } // optional& operator=(optional&& rhs) noexcept { // ref = rhs.ref; // return *this; // } template auto operator=(U&& rhs) noexcept -> typename enable_if < is_same::type, optional>::value, optional& >::type { ref = rhs.ref; return *this; } template auto operator=(U&& rhs) noexcept -> typename enable_if < !is_same::type, optional>::value, optional& >::type = delete; void emplace(T& v) noexcept { ref = static_addressof(v); } void emplace(T&&) = delete; void swap(optional& rhs) noexcept { std::swap(ref, rhs.ref); } // 20.5.5.3, observers constexpr T* operator->() const { return ASSERTED_EXPRESSION(ref, ref); } constexpr T& operator*() const { return ASSERTED_EXPRESSION(ref, *ref); } constexpr T& value() const { return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref); } #if !OPTIONAL_GCC44_COMPATIBILITY explicit constexpr #endif operator bool() const noexcept { return ref != nullptr; } template constexpr typename decay::type value_or(V&& v) const { return *this ? **this : static_cast::type>(constexpr_forward(v)); } }; template class optional { static_assert( sizeof(T) == 0, "optional rvalue referencs disallowed" ); }; // 20.5.8, Relational operators template constexpr bool operator==(const optional& x, const optional& y) { return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; } template constexpr bool operator!=(const optional& x, const optional& y) { return !(x == y); } template constexpr bool operator<(const optional& x, const optional& y) { return (!y) ? false : (!x) ? true : *x < *y; } template constexpr bool operator>(const optional& x, const optional& y) { return (y < x); } template constexpr bool operator<=(const optional& x, const optional& y) { return !(y < x); } template constexpr bool operator>=(const optional& x, const optional& y) { return !(x < y); } // 20.5.9 Comparison with nullopt template constexpr bool operator==(const optional& x, nullopt_t) noexcept { return (!x); } template constexpr bool operator==(nullopt_t, const optional& x) noexcept { return (!x); } template constexpr bool operator!=(const optional& x, nullopt_t) noexcept { return bool(x); } template constexpr bool operator!=(nullopt_t, const optional& x) noexcept { return bool(x); } template constexpr bool operator<(const optional&, nullopt_t) noexcept { return false; } template constexpr bool operator<(nullopt_t, const optional& x) noexcept { return bool(x); } template constexpr bool operator<=(const optional& x, nullopt_t) noexcept { return (!x); } template constexpr bool operator<=(nullopt_t, const optional&) noexcept { return true; } template constexpr bool operator>(const optional& x, nullopt_t) noexcept { return bool(x); } template constexpr bool operator>(nullopt_t, const optional&) noexcept { return false; } template constexpr bool operator>=(const optional&, nullopt_t) noexcept { return true; } template constexpr bool operator>=(nullopt_t, const optional& x) noexcept { return (!x); } // 20.5.10, Comparison with T template constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } template constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } template constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } template constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } template constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } template constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } template constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } template constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } template constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } template constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } template constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } template constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } // Comparison of optionsl with T template constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } template constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } template constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } template constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } template constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } template constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } template constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } template constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } template constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } template constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } template constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } template constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } // Comparison of optionsl with T template constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } template constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } template constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } template constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } template constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } template constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } template constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } template constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } template constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } template constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } template constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } template constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } // 20.5.12 Specialized algorithms template void swap(optional& x, optional& y) #if !OPTIONAL_GCC45_COMPATIBILITY noexcept(noexcept(x.swap(y))) #endif { x.swap(y); } template constexpr optional::type> make_optional(T&& v) { return optional::type>(constexpr_forward(v)); } template constexpr optional make_optional(reference_wrapper v) { return optional(v.get()); } } // namespace std namespace std { template struct hash> { typedef typename hash::result_type result_type; typedef std::optional argument_type; constexpr result_type operator()(argument_type const& arg) const { return arg ? std::hash{}(*arg) : result_type{}; } }; template struct hash> { typedef typename hash::result_type result_type; typedef std::optional argument_type; constexpr result_type operator()(argument_type const& arg) const { return arg ? std::hash{}(*arg) : result_type{}; } }; } #ifdef OptionalBase # undef OptionalBase #endif # endif //___OPTIONAL_HPP___