#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 // 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 template struct MAPBOX_VARIANT_DEPRECATED static_visitor { using result_type = R; protected: static_visitor() {} ~static_visitor() {} }; namespace detail { static constexpr std::size_t invalid_value = std::size_t(-1); template struct direct_type; template struct direct_type { static constexpr std::size_t index = std::is_same::value ? sizeof...(Types) : direct_type::index; }; template struct direct_type { static constexpr std::size_t index = invalid_value; }; #if __cpp_lib_logical_traits >= 201510L using std::disjunction; #else 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 std::size_t index = std::is_convertible::value ? disjunction...>::value ? invalid_value : sizeof...(Types) : convertible_type::index; }; template struct convertible_type { static constexpr std::size_t index = invalid_value; }; template struct value_traits { using value_type = typename std::remove_const::type>::type; static constexpr std::size_t direct_index = direct_type::index; static constexpr bool is_direct = direct_index != invalid_value; static constexpr std::size_t index = is_direct ? direct_index : convertible_type::index; static constexpr bool is_valid = index != invalid_value; static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0; using target_type = typename std::tuple_element>::type; }; template struct enable_if_type { using type = R; }; template struct result_of_unary_visit { using type = typename std::result_of::type; }; template struct result_of_unary_visit::type> { using type = typename F::result_type; }; template struct result_of_binary_visit { using type = typename std::result_of::type; }; template struct result_of_binary_visit::type> { using type = typename F::result_type; }; template struct static_max; template struct static_max { static const std::size_t value = arg; }; template struct static_max { static const std::size_t value = arg1 >= arg2 ? static_max::value : static_max::value; }; template struct variant_helper; template struct variant_helper { VARIANT_INLINE static void destroy(const std::size_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 std::size_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 std::size_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 std::size_t, void*) {} VARIANT_INLINE static void move(const std::size_t, void*, void*) {} VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {} }; template struct unwrapper { static T const& apply_const(T const& obj) { return obj; } static T& apply(T& obj) { return obj; } }; template struct unwrapper> { static auto apply_const(recursive_wrapper const& obj) -> typename recursive_wrapper::type const& { return obj.get(); } static auto apply(recursive_wrapper& obj) -> typename recursive_wrapper::type& { return obj.get(); } }; template struct unwrapper> { static auto apply_const(std::reference_wrapper const& obj) -> typename std::reference_wrapper::type const& { return obj.get(); } static auto apply(std::reference_wrapper& obj) -> typename std::reference_wrapper::type& { return obj.get(); } }; template struct dispatcher; template struct dispatcher { VARIANT_INLINE static R apply_const(V const& v, F&& f) { if (v.template is()) { return f(unwrapper::apply_const(v.template get_unchecked())); } else { return dispatcher::apply_const(v, std::forward(f)); } } VARIANT_INLINE static R apply(V& v, F&& f) { if (v.template is()) { return f(unwrapper::apply(v.template get_unchecked())); } else { return dispatcher::apply(v, std::forward(f)); } } }; template struct dispatcher { VARIANT_INLINE static R apply_const(V const& v, F&& f) { return f(unwrapper::apply_const(v.template get_unchecked())); } VARIANT_INLINE static R apply(V& v, F&& f) { return f(unwrapper::apply(v.template get_unchecked())); } }; template struct binary_dispatcher_rhs; template struct binary_dispatcher_rhs { VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { if (rhs.template is()) // call binary functor { return f(unwrapper::apply_const(lhs.template get_unchecked()), unwrapper::apply_const(rhs.template get_unchecked())); } else { return binary_dispatcher_rhs::apply_const(lhs, rhs, std::forward(f)); } } VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { if (rhs.template is()) // call binary functor { return f(unwrapper::apply(lhs.template get_unchecked()), unwrapper::apply(rhs.template get_unchecked())); } else { return binary_dispatcher_rhs::apply(lhs, rhs, std::forward(f)); } } }; template struct binary_dispatcher_rhs { VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { return f(unwrapper::apply_const(lhs.template get_unchecked()), unwrapper::apply_const(rhs.template get_unchecked())); } VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { return f(unwrapper::apply(lhs.template get_unchecked()), unwrapper::apply(rhs.template get_unchecked())); } }; template struct binary_dispatcher_lhs; template struct binary_dispatcher_lhs { VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { if (lhs.template is()) // call binary functor { return f(unwrapper::apply_const(lhs.template get_unchecked()), unwrapper::apply_const(rhs.template get_unchecked())); } else { return binary_dispatcher_lhs::apply_const(lhs, rhs, std::forward(f)); } } VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { if (lhs.template is()) // call binary functor { return f(unwrapper::apply(lhs.template get_unchecked()), unwrapper::apply(rhs.template get_unchecked())); } else { return binary_dispatcher_lhs::apply(lhs, rhs, std::forward(f)); } } }; template struct binary_dispatcher_lhs { VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) { return f(unwrapper::apply_const(lhs.template get_unchecked()), unwrapper::apply_const(rhs.template get_unchecked())); } VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) { return f(unwrapper::apply(lhs.template get_unchecked()), unwrapper::apply(rhs.template get_unchecked())); } }; template struct binary_dispatcher; template struct binary_dispatcher { VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f) { if (v0.template is()) { if (v1.template is()) { return f(unwrapper::apply_const(v0.template get_unchecked()), unwrapper::apply_const(v1.template get_unchecked())); // call binary functor } else { return binary_dispatcher_rhs::apply_const(v0, v1, std::forward(f)); } } else if (v1.template is()) { return binary_dispatcher_lhs::apply_const(v0, v1, std::forward(f)); } return binary_dispatcher::apply_const(v0, v1, std::forward(f)); } VARIANT_INLINE static R apply(V& v0, V& v1, F&& f) { if (v0.template is()) { if (v1.template is()) { return f(unwrapper::apply(v0.template get_unchecked()), unwrapper::apply(v1.template get_unchecked())); // call binary functor } else { return binary_dispatcher_rhs::apply(v0, v1, std::forward(f)); } } else if (v1.template is()) { return binary_dispatcher_lhs::apply(v0, v1, std::forward(f)); } return binary_dispatcher::apply(v0, v1, std::forward(f)); } }; template struct binary_dispatcher { VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f) { return f(unwrapper::apply_const(v0.template get_unchecked()), unwrapper::apply_const(v1.template get_unchecked())); // call binary functor } VARIANT_INLINE static R apply(V& v0, V& v1, F&& f) { return f(unwrapper::apply(v0.template get_unchecked()), unwrapper::apply(v1.template get_unchecked())); // 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?"); 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 data_type = typename std::aligned_storage::type; using helper_type = detail::variant_helper; std::size_t type_index; data_type data; 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(std::is_nothrow_move_constructible::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) { move_assign(std::move(other)); return *this; } VARIANT_INLINE variant& operator=(variant const& other) { copy_assign(other); return *this; } // conversions // move-assign template VARIANT_INLINE variant& operator=(T&& rhs) noexcept { 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 std::size_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 ::type> auto VARIANT_INLINE static visit(V const& v, F&& f) -> decltype(detail::dispatcher::apply_const(v, std::forward(f))) { return detail::dispatcher::apply_const(v, std::forward(f)); } // non-const template ::type> auto VARIANT_INLINE static visit(V& v, F&& f) -> decltype(detail::dispatcher::apply(v, std::forward(f))) { return detail::dispatcher::apply(v, std::forward(f)); } // binary // const template ::type> auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F&& f) -> decltype(detail::binary_dispatcher::apply_const(v0, v1, std::forward(f))) { return detail::binary_dispatcher::apply_const(v0, v1, std::forward(f)); } // non-const template ::type> auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F&& f) -> decltype(detail::binary_dispatcher::apply(v0, v1, std::forward(f))) { return detail::binary_dispatcher::apply(v0, 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)...)); } ~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 // const template auto VARIANT_INLINE apply_visitor(F&& f, V const& v) -> decltype(V::visit(v, std::forward(f))) { return V::visit(v, std::forward(f)); } // non-const template auto VARIANT_INLINE apply_visitor(F&& f, V& v) -> decltype(V::visit(v, std::forward(f))) { return V::visit(v, std::forward(f)); } // binary visitor interface // const template auto VARIANT_INLINE apply_visitor(F&& f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward(f))) { return V::binary_visit(v0, v1, std::forward(f)); } // non-const template auto VARIANT_INLINE apply_visitor(F&& f, V& v0, V& v1) -> decltype(V::binary_visit(v0, v1, std::forward(f))) { return V::binary_visit(v0, 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(); } } // 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