#ifndef MAPBOX_UTIL_VARIANT_HPP #define MAPBOX_UTIL_VARIANT_HPP #include #include // size_t #include // operator new #include // runtime_error #include #include #include #include #include #include #include #include #include // clang-format off // [[deprecated]] is only available in C++14, use this for the time being #if __cplusplus <= 201103L # ifdef __GNUC__ # define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated)) # elif defined(_MSC_VER) # define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated) # else # define MAPBOX_VARIANT_DEPRECATED # endif #else # define MAPBOX_VARIANT_DEPRECATED [[deprecated]] #endif #ifdef _MSC_VER // https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx # ifdef NDEBUG # define VARIANT_INLINE __forceinline # else # define VARIANT_INLINE //__declspec(noinline) # endif #else # ifdef NDEBUG # define VARIANT_INLINE //inline __attribute__((always_inline)) # else # define VARIANT_INLINE __attribute__((noinline)) # endif #endif // clang-format on // Exceptions #if defined( __EXCEPTIONS) || defined( _MSC_VER) #define HAS_EXCEPTIONS #endif #define VARIANT_MAJOR_VERSION 1 #define VARIANT_MINOR_VERSION 1 #define VARIANT_PATCH_VERSION 0 #define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION) namespace mapbox { namespace util { // XXX This should derive from std::logic_error instead of std::runtime_error. // See https://github.com/mapbox/variant/issues/48 for details. class bad_variant_access : public std::runtime_error { public: explicit bad_variant_access(const std::string& what_arg) : runtime_error(what_arg) {} explicit bad_variant_access(const char* what_arg) : runtime_error(what_arg) {} }; // class bad_variant_access #if !defined(MAPBOX_VARIANT_MINIMIZE_SIZE) using type_index_t = unsigned int; #else #if defined(MAPBOX_VARIANT_OPTIMIZE_FOR_SPEED) using type_index_t = std::uint_fast8_t; #else using type_index_t = std::uint_least8_t; #endif #endif namespace detail { static constexpr type_index_t invalid_value = type_index_t(-1); template struct direct_type; template struct direct_type { static constexpr type_index_t index = std::is_same::value ? sizeof...(Types) : direct_type::index; }; template struct direct_type { static constexpr type_index_t index = invalid_value; }; #if __cpp_lib_logical_traits >= 201510L using std::conjunction; using std::disjunction; #else template struct conjunction : std::true_type {}; template struct conjunction : B1 {}; template struct conjunction : std::conditional::type {}; template struct conjunction : std::conditional, B1>::type {}; template struct disjunction : std::false_type {}; template struct disjunction : B1 {}; template struct disjunction : std::conditional::type {}; template struct disjunction : std::conditional>::type {}; #endif template struct convertible_type; template struct convertible_type { static constexpr type_index_t index = std::is_convertible::value ? disjunction...>::value ? invalid_value : sizeof...(Types) : convertible_type::index; }; template struct convertible_type { static constexpr type_index_t index = invalid_value; }; template struct value_traits { using value_type = typename std::remove_const::type>::type; using value_type_wrapper = recursive_wrapper; static constexpr type_index_t direct_index = direct_type::index; static constexpr bool is_direct = direct_index != invalid_value; static constexpr type_index_t index_direct_or_wrapper = is_direct ? direct_index : direct_type::index; static constexpr bool is_direct_or_wrapper = index_direct_or_wrapper != invalid_value; static constexpr type_index_t index = is_direct_or_wrapper ? index_direct_or_wrapper : convertible_type::index; static constexpr bool is_valid = index != invalid_value; static constexpr type_index_t tindex = is_valid ? sizeof...(Types)-index : 0; using target_type = typename std::tuple_element>::type; }; template struct copy_cvref { using type = Dest; }; template struct copy_cvref { using type = Dest const&; }; template struct copy_cvref { using type = Dest&; }; template struct copy_cvref { using type = Dest&&; }; template struct deduced_result_type {}; template struct deduced_result_type()(std::declval()...))> { using type = decltype(std::declval()(std::declval()...)); }; template struct visitor_result_type : deduced_result_type {}; // specialization for explicit result_type member in visitor class template struct visitor_result_type::type::result_type>())> { using type = typename std::decay::type::result_type; }; template using result_of_unary_visit = typename visitor_result_type::type; template using result_of_binary_visit = typename visitor_result_type::type; template struct static_max; template struct static_max { static const type_index_t value = arg; }; template struct static_max { static const type_index_t value = arg1 >= arg2 ? static_max::value : static_max::value; }; template struct variant_helper; template struct variant_helper { VARIANT_INLINE static void destroy(const type_index_t type_index, void* data) { if (type_index == sizeof...(Types)) { reinterpret_cast(data)->~T(); } else { variant_helper::destroy(type_index, data); } } VARIANT_INLINE static void move(const type_index_t old_type_index, void* old_value, void* new_value) { if (old_type_index == sizeof...(Types)) { new (new_value) T(std::move(*reinterpret_cast(old_value))); } else { variant_helper::move(old_type_index, old_value, new_value); } } VARIANT_INLINE static void copy(const type_index_t old_type_index, const void* old_value, void* new_value) { if (old_type_index == sizeof...(Types)) { new (new_value) T(*reinterpret_cast(old_value)); } else { variant_helper::copy(old_type_index, old_value, new_value); } } }; template <> struct variant_helper<> { VARIANT_INLINE static void destroy(const type_index_t, void*) {} VARIANT_INLINE static void move(const type_index_t, void*, void*) {} VARIANT_INLINE static void copy(const type_index_t, const void*, void*) {} }; template struct unwrapper { using value_type = T; template static auto apply(typename std::remove_reference::type& var) -> typename std::enable_if::value, decltype(var.template get_unchecked())>::type { return var.template get_unchecked(); } template static auto apply(typename std::remove_reference::type& var) -> typename std::enable_if::value, decltype(std::move(var.template get_unchecked()))>::type { return std::move(var.template get_unchecked()); } }; template struct unwrapper> : unwrapper {}; template struct unwrapper> : unwrapper {}; template struct dispatcher; template struct dispatcher { template VARIANT_INLINE static R apply(V&& v, F&& f) { if (v.template is()) { return std::forward(f)(unwrapper::template apply(v)); } else { return dispatcher::apply(std::forward(v), std::forward(f)); } } }; template struct dispatcher { template VARIANT_INLINE static R apply(V&& v, F&& f) { return std::forward(f)(unwrapper::template apply(v)); } }; template struct binary_dispatcher_rhs; template struct binary_dispatcher_rhs { template VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { if (rhs.template is()) // call binary functor { return std::forward(f)(unwrapper::template apply(lhs), unwrapper::template apply(rhs)); } else { return binary_dispatcher_rhs::apply(std::forward(lhs), std::forward(rhs), std::forward(f)); } } }; template struct binary_dispatcher_rhs { template VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { return std::forward(f)(unwrapper::template apply(lhs), unwrapper::template apply(rhs)); } }; template struct binary_dispatcher_lhs; template struct binary_dispatcher_lhs { template VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { if (lhs.template is()) // call binary functor { return std::forward(f)(unwrapper::template apply(lhs), unwrapper::template apply(rhs)); } else { return binary_dispatcher_lhs::apply(std::forward(lhs), std::forward(rhs), std::forward(f)); } } }; template struct binary_dispatcher_lhs { template VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { return std::forward(f)(unwrapper::template apply(lhs), unwrapper::template apply(rhs)); } }; template struct binary_dispatcher; template struct binary_dispatcher { template VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) { if (v0.template is()) { if (v1.template is()) { return std::forward(f)(unwrapper::template apply(v0), unwrapper::template apply(v1)); // call binary functor } else { return binary_dispatcher_rhs::apply(std::forward(v0), std::forward(v1), std::forward(f)); } } else if (v1.template is()) { return binary_dispatcher_lhs::apply(std::forward(v0), std::forward(v1), std::forward(f)); } return binary_dispatcher::apply(std::forward(v0), std::forward(v1), std::forward(f)); } }; template struct binary_dispatcher { template VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) { return std::forward(f)(unwrapper::template apply(v0), unwrapper::template apply(v1)); // call binary functor } }; // comparator functors struct equal_comp { template bool operator()(T const& lhs, T const& rhs) const { return lhs == rhs; } }; struct less_comp { template bool operator()(T const& lhs, T const& rhs) const { return lhs < rhs; } }; template class comparer { public: explicit comparer(Variant const& lhs) noexcept : lhs_(lhs) {} comparer& operator=(comparer const&) = delete; // visitor template bool operator()(T const& rhs_content) const { T const& lhs_content = lhs_.template get_unchecked(); return Comp()(lhs_content, rhs_content); } private: Variant const& lhs_; }; // hashing visitor struct hasher { template std::size_t operator()(const T& hashable) const { return std::hash{}(hashable); } }; } // namespace detail struct no_init {}; template class variant { static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty."); static_assert(!detail::disjunction...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?"); static_assert(!detail::disjunction...>::value, "Variant can not hold array types."); static_assert(sizeof...(Types) < std::numeric_limits::max(), "Internal index type must be able to accommodate all alternatives."); private: static const std::size_t data_size = detail::static_max::value; static const std::size_t data_align = detail::static_max::value; public: struct adapted_variant_tag; using types = std::tuple; private: using first_type = typename std::tuple_element<0, types>::type; using unwrap_first_type = typename detail::unwrapper::value_type; using data_type = typename std::aligned_storage::type; using helper_type = detail::variant_helper; template using alternative_ref = typename detail::copy_cvref::type; type_index_t type_index; #ifdef __clang_analyzer__ data_type data {}; #else data_type data; #endif public: VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible::value) : type_index(sizeof...(Types)-1) { static_assert(std::is_default_constructible::value, "First type in variant must be default constructible to allow default construction of variant."); new (&data) first_type(); } VARIANT_INLINE variant(no_init) noexcept : type_index(detail::invalid_value) {} // http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers template , typename Enable = typename std::enable_if, typename Traits::value_type>::value>::type > VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible::value) : type_index(Traits::index) { new (&data) typename Traits::target_type(std::forward(val)); } VARIANT_INLINE variant(variant const& old) : type_index(old.type_index) { helper_type::copy(old.type_index, &old.data, &data); } VARIANT_INLINE variant(variant&& old) noexcept(detail::conjunction...>::value) : type_index(old.type_index) { helper_type::move(old.type_index, &old.data, &data); } private: VARIANT_INLINE void copy_assign(variant const& rhs) { helper_type::destroy(type_index, &data); type_index = detail::invalid_value; helper_type::copy(rhs.type_index, &rhs.data, &data); type_index = rhs.type_index; } VARIANT_INLINE void move_assign(variant&& rhs) { helper_type::destroy(type_index, &data); type_index = detail::invalid_value; helper_type::move(rhs.type_index, &rhs.data, &data); type_index = rhs.type_index; } public: VARIANT_INLINE variant& operator=(variant&& other) // note we check for nothrow-constructible, not nothrow-assignable, since // move_assign uses move-construction via placement new. noexcept(detail::conjunction...>::value) { if (this == &other) { // playing safe in release mode, hit assertion in debug. assert(false); return *this; } move_assign(std::move(other)); return *this; } VARIANT_INLINE variant& operator=(variant const& other) { if (this != &other) copy_assign(other); return *this; } // conversions // move-assign template , typename Enable = typename std::enable_if, typename Traits::value_type>::value>::type > VARIANT_INLINE variant& operator=(T&& rhs) // not that we check is_nothrow_constructible, not is_nothrow_move_assignable, // since we construct a temporary noexcept(std::is_nothrow_constructible::value && std::is_nothrow_move_assignable>::value) { variant temp(std::forward(rhs)); move_assign(std::move(temp)); return *this; } // copy-assign template VARIANT_INLINE variant& operator=(T const& rhs) { variant temp(rhs); copy_assign(temp); return *this; } template ::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE bool is() const { return type_index == detail::direct_type::index; } template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE bool is() const { return type_index == detail::direct_type, Types...>::index; } VARIANT_INLINE bool valid() const { return type_index != detail::invalid_value; } template VARIANT_INLINE void set(Args&&... args) { helper_type::destroy(type_index, &data); type_index = detail::invalid_value; new (&data) T(std::forward(args)...); type_index = detail::direct_type::index; } // get_unchecked() template ::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T& get_unchecked() { return *reinterpret_cast(&data); } #ifdef HAS_EXCEPTIONS // get() template ::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T& get() { if (type_index == detail::direct_type::index) { return *reinterpret_cast(&data); } else { throw bad_variant_access("in get()"); } } #endif template ::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T const& get_unchecked() const { return *reinterpret_cast(&data); } #ifdef HAS_EXCEPTIONS template ::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T const& get() const { if (type_index == detail::direct_type::index) { return *reinterpret_cast(&data); } else { throw bad_variant_access("in get()"); } } #endif // get_unchecked() - T stored as recursive_wrapper template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T& get_unchecked() { return (*reinterpret_cast*>(&data)).get(); } #ifdef HAS_EXCEPTIONS // get() - T stored as recursive_wrapper template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T& get() { if (type_index == detail::direct_type, Types...>::index) { return (*reinterpret_cast*>(&data)).get(); } else { throw bad_variant_access("in get()"); } } #endif template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T const& get_unchecked() const { return (*reinterpret_cast const*>(&data)).get(); } #ifdef HAS_EXCEPTIONS template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T const& get() const { if (type_index == detail::direct_type, Types...>::index) { return (*reinterpret_cast const*>(&data)).get(); } else { throw bad_variant_access("in get()"); } } #endif // get_unchecked() - T stored as std::reference_wrapper template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T& get_unchecked() { return (*reinterpret_cast*>(&data)).get(); } #ifdef HAS_EXCEPTIONS // get() - T stored as std::reference_wrapper template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T& get() { if (type_index == detail::direct_type, Types...>::index) { return (*reinterpret_cast*>(&data)).get(); } else { throw bad_variant_access("in get()"); } } #endif template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T const& get_unchecked() const { return (*reinterpret_cast const*>(&data)).get(); } #ifdef HAS_EXCEPTIONS template , Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T const& get() const { if (type_index == detail::direct_type, Types...>::index) { return (*reinterpret_cast const*>(&data)).get(); } else { throw bad_variant_access("in get()"); } } #endif // This function is deprecated because it returns an internal index field. // Use which() instead. MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE type_index_t get_type_index() const { return type_index; } VARIANT_INLINE int which() const noexcept { return static_cast(sizeof...(Types) - type_index - 1); } template ::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE static constexpr int which() noexcept { return static_cast(sizeof...(Types)-detail::direct_type::index - 1); } // visitor // unary template , typename R = detail::result_of_unary_visit> VARIANT_INLINE static R visit(V&& v, F&& f) { return detail::dispatcher::apply(std::forward(v), std::forward(f)); } // binary template , typename R = detail::result_of_binary_visit> VARIANT_INLINE static R binary_visit(V&& v0, V&& v1, F&& f) { return detail::binary_dispatcher::apply(std::forward(v0), std::forward(v1), std::forward(f)); } // match // unary template auto VARIANT_INLINE match(Fs&&... fs) const& -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) { return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); } // non-const template auto VARIANT_INLINE match(Fs&&... fs) & -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) { return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); } template auto VARIANT_INLINE match(Fs&&... fs) && -> decltype(variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...))) { return variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...)); } ~variant() noexcept // no-throw destructor { helper_type::destroy(type_index, &data); } // comparison operators // equality VARIANT_INLINE bool operator==(variant const& rhs) const { assert(valid() && rhs.valid()); if (this->which() != rhs.which()) { return false; } detail::comparer visitor(*this); return visit(rhs, visitor); } VARIANT_INLINE bool operator!=(variant const& rhs) const { return !(*this == rhs); } // less than VARIANT_INLINE bool operator<(variant const& rhs) const { assert(valid() && rhs.valid()); if (this->which() != rhs.which()) { return this->which() < rhs.which(); } detail::comparer visitor(*this); return visit(rhs, visitor); } VARIANT_INLINE bool operator>(variant const& rhs) const { return rhs < *this; } VARIANT_INLINE bool operator<=(variant const& rhs) const { return !(*this > rhs); } VARIANT_INLINE bool operator>=(variant const& rhs) const { return !(*this < rhs); } }; // unary visitor interface template auto VARIANT_INLINE apply_visitor(F&& f, V&& v) -> decltype(v.visit(std::forward(v), std::forward(f))) { return v.visit(std::forward(v), std::forward(f)); } // binary visitor interface template auto VARIANT_INLINE apply_visitor(F&& f, V&& v0, V&& v1) -> decltype(v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f))) { return v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f)); } // getter interface #ifdef HAS_EXCEPTIONS template auto get(T& var)->decltype(var.template get()) { return var.template get(); } #endif template ResultType& get_unchecked(T& var) { return var.template get_unchecked(); } #ifdef HAS_EXCEPTIONS template auto get(T const& var)->decltype(var.template get()) { return var.template get(); } #endif template ResultType const& get_unchecked(T const& var) { return var.template get_unchecked(); } // variant_size template struct variant_size; //variable templates is c++14 //template //constexpr std::size_t variant_size_v = variant_size::value; template struct variant_size : variant_size {}; template struct variant_size : variant_size {}; template struct variant_size : variant_size {}; template struct variant_size> : std::integral_constant {}; // variant_alternative template struct variant_alternative; #if defined(__clang__) #if __has_builtin(__type_pack_element) #define has_type_pack_element #endif #endif #if defined(has_type_pack_element) template struct variant_alternative> { static_assert(sizeof...(Types) > Index , "Index out of range"); using type = __type_pack_element; }; #else template struct variant_alternative> : variant_alternative> { static_assert(sizeof...(Types) > Index -1 , "Index out of range"); }; template struct variant_alternative<0, variant> { using type = First; }; #endif template using variant_alternative_t = typename variant_alternative::type; template struct variant_alternative : std::add_const> {}; template struct variant_alternative : std::add_volatile> {}; template struct variant_alternative : std::add_cv> {}; } // namespace util } // namespace mapbox // hashable iff underlying types are hashable namespace std { template struct hash< ::mapbox::util::variant> { std::size_t operator()(const ::mapbox::util::variant& v) const noexcept { return ::mapbox::util::apply_visitor(::mapbox::util::detail::hasher{}, v); } }; } #endif // MAPBOX_UTIL_VARIANT_HPP