fatal/container/legacy_variant.h (1,523 lines of code) (raw):

/* * Copyright (c) 2016, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #ifndef FATAL_INCLUDE_fatal_container_legacy_variant_h #define FATAL_INCLUDE_fatal_container_legacy_variant_h #include <fatal/container/optional.h> #include <fatal/container/unitary_union.h> #include <fatal/functional/functional.h> #include <fatal/math/numerics.h> #include <fatal/portability.h> #include <fatal/type/conditional.h> #include <fatal/type/deprecated/type_list.h> #include <fatal/type/traits.h> #include <functional> #include <memory> #include <utility> #include <cassert> FATAL_DIAGNOSTIC_PUSH FATAL_GCC_DIAGNOSTIC_IGNORED_SHADOW_IF_BROKEN /** * ################# * # READ ME FIRST # * ################# * * This file implements a memory efficient custom allocated variant. * * NOTE: this implementation is undergoing a major cleanup. * The interface should be considered mostly stable, though. * * This implementation is rather complex and involves lots of compile-time * template metaprogramming so. To keep it simple you're encouraged to start * with the variant declaration itself before anything else. * * To quickly get there, search for 'START HERE'. * * @author: Marcelo Juchem <marcelo@fb.com> */ namespace fatal { /** * ####################### * # 'ALLOCATION POLICY' # * ####################### * * This is the default implementation for the allocation policy * given a type T it decides whether to allocate it automatically * or dynamically. * * A type T is automatically allocated if it is a scalar, or if its * size in bytes is smaller than ax + b, where x is the size in bytes * of a pointer to T, a is allocateMultiplier (defaults to 1) and * b is allocateIncrement (defaults to the size in bytes of size_t). * * @author: Marcelo Juchem <marcelo@fb.com> */ namespace detail { template <typename std::size_t Threshold> struct variant_automatic_policy_impl { template < typename T, typename = enable_when::any_true< std::is_scalar<T>, bool_constant<(sizeof(T) <= Threshold)> > > static std::true_type sfinae(T *); template <typename...> static std::false_type sfinae(...); template <typename T> using type = logical::all< is_complete<T>, decltype(sfinae(static_cast<T *>(nullptr))) >; }; } // namespace detail { template < std::size_t allocateMultiplier = 1, std::size_t allocateIncrement = sizeof(std::size_t) > class default_allocation_policy { template <typename T> using allocate_threshold = size_constant< allocateMultiplier * sizeof(void *) + allocateIncrement >; public: template <typename T> using dynamic = bool_constant<!( detail::variant_automatic_policy_impl<allocate_threshold<T>::value> ::template type<T>::value )>; }; template <bool AlwaysDynamic> class fixed_allocation_policy { template <typename T> struct impl { static_assert( AlwaysDynamic || is_complete<T>::value, "can't allocate an incomplete type using automatic storage duration" ); using type = bool_constant<AlwaysDynamic>; }; public: template <typename T> using dynamic = typename impl<T>::type; }; using dynamic_allocation_policy = fixed_allocation_policy<true>; using automatic_allocation_policy = fixed_allocation_policy<false>; /** * #################### * # 'STORAGE POLICY' # * #################### * * This is the default implementation of the storage policy. Its goal is to * orchestrate the interactions with both the allocation policy and the * allocator, providing a uniform interface for the underlying storage * used to store the values. * * This policy provides the following methods: * * - these methods are analogous to std::allocator_traits' ones: * * static void allocate(Alloc const &, typename storage_type<T>::type &); * static void deallocate(Alloc const &, typename storage_type<T>::type &); * static T &construct(Alloc const &, typename storage_type<T>::type &, ...); * static void destroy(Alloc const &, typename storage_type<T>::type &); * * - move_over() is provided to aid implementation of move construction, move * assignment and swapping: * * static void move_over( * typename storage_type<T>::type &from * typename storage_type<T>::type &to * ); * * - get() is provided to abstract the way the actual stored value is obtained, * from its internal storage representation: * * static T const &get(typename storage_type<T>::type const &); * static T &get(typename storage_type<T>::type &); * * TAllocator is any STL-style allocator which will be rebound to whatever * type the storage policy must handle. It will only be used for dynamically * allocated types. * * TAllocationPolicy decides which allocation method will be used for a given * type. It needs to provide a single constant * * template <typename T> using dynamic = std::integral_constant<bool, ???>; * * which returns true when dynamic allocation must be used to store the type * T, or false when automatic allocation must be used to store the same type. * * A default implementation of the allocation policy is provided. Search for * 'ALLOCATION POLICY' for more details. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename TAllocator = std::allocator<void>, typename TAllocationPolicy = default_allocation_policy<>, bool IsCopyable = true > struct legacy_storage_policy { using allocator_type = TAllocator; using allocator_traits = std::allocator_traits<allocator_type>; using allocation_policy = TAllocationPolicy; /** * Tells whether copy construction and copy assignment should be allowed by * the variant. Checked at compile time. */ constexpr static bool is_copyable() { return IsCopyable; } template <typename T> using allocate_dynamically = typename allocation_policy::template dynamic<T>; /** * This class abstracts the interaction with TAllocationPolicy. It provides * a member type for the internal storage representation of the given type T, * and a boolean telling whether this representation uses automatic or * dynamic storage. */ template <typename T> class storage_type { static_assert( !std::is_reference<T>::value, "default_storage_policy: unable to store references" ); public: using dynamic = allocate_dynamically<T>; static_assert( is_complete<T>::value || dynamic::value, "can't allocate an incomplete type using automatic storage duration" ); // A naked pointer is used for dynamically allocated types. No smart // pointers are used since we aim for the smallest possible footprint // (having a copy of the deleter for every pointer is undesired). using type = conditional< dynamic::value, T *, unitary_union<T, false> >; }; // Methods for when T is stored using DYNAMIC ALLOCATION. // // The methods below are guaranteed to work on `T *` due to: // typename std::enable_if< // storage_type<T>::dynamic::value, // ... template <typename T, typename std::enable_if< storage_type<T>::dynamic::value, int >::type = 0> static void allocate( allocator_type const &allocator, typename storage_type<T>::type &storage ) { using rebound_alloc = typename allocator_traits::template rebind_alloc<T>; storage = rebound_alloc(allocator).allocate(1); } template <typename T, typename std::enable_if< storage_type<T>::dynamic::value, int >::type = 0> static void deallocate( allocator_type const &allocator, typename storage_type<T>::type &storage ) { using rebound_alloc = typename allocator_traits::template rebind_alloc<T>; rebound_alloc(allocator).deallocate(storage, 1); storage = nullptr; } template <typename T, typename... Args, typename std::enable_if< storage_type<T>::dynamic::value, int >::type = 0> static T &construct( allocator_type const &allocator, typename storage_type<T>::type &storage, Args &&...args ) { using rebound_traits = typename allocator_traits::template rebind_traits<T>; auto allocator_ = typename rebound_traits::allocator_type(allocator); rebound_traits::construct(allocator_, storage, std::forward<Args>(args)...); return *storage; } template <typename T, typename std::enable_if< storage_type<T>::dynamic::value, int >::type = 0> static void destroy( allocator_type const &allocator, typename storage_type<T>::type &storage ) { using rebound_traits = typename allocator_traits::template rebind_traits<T>; auto allocator_ = typename rebound_traits::allocator_type(allocator); rebound_traits::destroy(allocator_, storage); } template <typename T, typename std::enable_if< storage_type<T>::dynamic::value, int >::type = 0> static void move_over( typename storage_type<T>::type &from, typename storage_type<T>::type &to ) { to = from; from = nullptr; } template <typename T, typename std::enable_if< storage_type<T>::dynamic::value, int >::type = 0> static T const &get( typename storage_type<T>::type const &storage ) { return *storage; } template <typename T, typename std::enable_if< storage_type<T>::dynamic::value, int >::type = 0> static T &get( typename storage_type<T>::type &storage ) { return *storage; } // Methods for when T is stored using AUTOMATIC ALLOCATION. // // The methods below are guaranteed to work on `union { T value; };` due to: // typename std::enable_if< // !storage_type<T>::dynamic::value, // ... template <typename T, typename std::enable_if< !storage_type<T>::dynamic::value, int >::type = 0> static void allocate( allocator_type const &, typename storage_type<T>::type & ) {} template <typename T, typename std::enable_if< !storage_type<T>::dynamic::value, int >::type = 0> static void deallocate( allocator_type const &, typename storage_type<T>::type & ) {} template <typename T, typename... Args, typename std::enable_if< !storage_type<T>::dynamic::value, int >::type = 0> static T &construct( allocator_type const &, typename storage_type<T>::type &storage, Args &&...args ) { return *new (std::addressof(storage.value)) T(std::forward<Args>(args)...); } template <typename T, typename std::enable_if< !storage_type<T>::dynamic::value, int >::type = 0> static void destroy( allocator_type const &, typename storage_type<T>::type &storage ) { storage.value.~T(); } template <typename T, typename std::enable_if< !storage_type<T>::dynamic::value, int >::type = 0> static void move_over( typename storage_type<T>::type &from, typename storage_type<T>::type &to ) { new (std::addressof(to.value)) T(std::move(from.value)); from.value.~T(); } template <typename T, typename std::enable_if< !storage_type<T>::dynamic::value, int >::type = 0> static T const &get( typename storage_type<T>::type const &storage ) { return storage.value; } template <typename T, typename std::enable_if< !storage_type<T>::dynamic::value, int >::type = 0> static T &get( typename storage_type<T>::type &storage ) { return storage.value; } }; template < typename Allocator = std::allocator<void>, typename AllocationPolicy = default_allocation_policy<>, bool IsCopyable = true > using default_storage_policy = legacy_storage_policy< Allocator, AllocationPolicy, IsCopyable >; namespace detail { namespace variant_impl { /** * An internal traits class used by fatal::variant to handle variadic * recursion and runtime type erasure without the need for virtual calls. * * It basically provides a union comprised of all the types that need to * be stored and a uniform interface to handle such union in a type-safe * manner. * * Try to picture the goal of (some parameters omitted for simplicity) * * variadic_union_traits<T1, T2, T3, ..., Tn-1, Tn> * * as providing something like * * union { * head: union { storage_policy::storage_type<T1>::type storage; }; * tail: union { * head: union { storage_policy::storage_type<T2>::type storage; }; * tail: union { * head: union { storage_policy::storage_type<T3>::type storage; }; * tail: union { * ... * head: union { storage_policy::storage_type<Tn-1>::type storage; }; * tail: union { storage_policy::storage_type<Tn>::type storage; }; * }; * }; * }; * }; * * There are two specializations for this class. One of them is the general * variadic version (a.k.a. general case) and the other is the terminal * version (a.k.a. base case). * * - General case: it is responsible for checking whether the given type * relates to the current recursion depth or not: * - if it does, it delegates the call to a terminal (base case) * representing the current recursion depth (head); * - if not, it propagates the call further deep in the recursion (tail). * * - Base case: it is responsible for interfacing with the storage policy * and effectivelly executing any necessary operations on the storage. * * Each of the recursion levels are identified by an unique number, a.k.a * the type_tag, which is the information used both at compile and run-time * by the general case to determine the right depth of the union that must * be operated upon. * * @author: Marcelo Juchem <marcelo@fb.com> */ template <typename, typename TSize, TSize, typename...> struct variadic_union_traits; // empty case template <typename TStoragePolicy, typename TSize, TSize Depth> struct variadic_union_traits<TStoragePolicy, TSize, Depth> { using size_type = TSize; union union_type {}; constexpr static size_type end_depth() { return Depth; } }; // base case template <typename TStoragePolicy, typename TSize, TSize Depth, typename T> struct variadic_union_traits<TStoragePolicy, TSize, Depth, T> { static_assert(std::is_integral<TSize>::value, "TSize must be an integral"); static_assert(!std::is_reference<T>::value, "T can't be a reference"); static_assert(!std::is_const<T>::value, "T can't be const"); using size_type = TSize; using value_type = T; using storage_policy = TStoragePolicy; using allocator_type = typename storage_policy::allocator_type; using storage_type = typename storage_policy ::template storage_type<value_type>::type; union union_type { storage_type storage; union_type() {} union_type(union_type const &) {} union_type(union_type &&) {} ~union_type() {} }; // calls not dependent on value_type constexpr static size_type end_depth() { return Depth + 1; } template <template <typename> class... TTransforms> static std::type_info const &type(size_type const depth) { assert(depth == Depth), (void) depth; return typeid( typename fatal::try_transform< fatal::compose<TTransforms...>::template apply >::template apply<value_type> ); } template <typename TVisitor, typename... UArgs> static void visit( size_type const depth, union_type const &u, TVisitor &&visitor, UArgs &&...args ) { assert(depth == Depth), (void) depth; visitor( storage_policy::template get<value_type>(u.storage), std::forward<UArgs>(args)... ); } template <typename TVisitor, typename... UArgs> static void visit( size_type const depth, union_type &u, TVisitor &&visitor, UArgs &&...args ) { assert(depth == Depth), (void) depth; visitor( storage_policy::template get<value_type>(u.storage), std::forward<UArgs>(args)... ); } static void allocate( allocator_type const &allocator, size_type const depth, union_type &u ) { assert(depth == Depth), (void) depth; storage_policy::template allocate<value_type>(allocator, u.storage); } static void deallocate( allocator_type const &allocator, size_type const depth, union_type &u ) { assert(depth == Depth), (void) depth; storage_policy::template deallocate<value_type>(allocator, u.storage); } static void copy_construct( allocator_type const &allocator, size_type const depth, union_type const &from, union_type &to ) { assert(depth == Depth), (void) depth; storage_policy::template construct<value_type>( allocator, to.storage, storage_policy::template get<value_type>(from.storage) ); } static void destroy( allocator_type const &allocator, size_type const depth, union_type &u ) { assert(depth == Depth), (void) depth; storage_policy::template destroy<value_type>(allocator, u.storage); } static void move_over( size_type const depth, union_type &from, union_type &to ) { assert(depth == Depth), (void) depth; storage_policy::template move_over<value_type>(from.storage, to.storage); } static bool equals( size_type const depth, union_type const &lhs, union_type const &rhs ) { if (depth != Depth) { assert(depth == end_depth()); return true; } return storage_policy::template get<value_type>(lhs.storage) == storage_policy::template get<value_type>(rhs.storage); } static bool less_than( size_type const depth, union_type const &lhs, union_type const &rhs ) { if (depth != Depth) { assert(depth == end_depth()); return false; } return storage_policy::template get<value_type>(lhs.storage) < storage_policy::template get<value_type>(rhs.storage); } // templated calls dependent on value_type // returns end_depth() when U is not found template <typename U> constexpr static size_type get_depth() { return std::is_same<U, value_type>::value ? Depth : end_depth(); } template <typename U, typename std::enable_if< std::is_same<U, value_type>::value, int >::type = 0> static storage_type const &get(union_type const &u) { return u.storage; } template <typename U, typename std::enable_if< std::is_same<U, value_type>::value, int >::type = 0> static storage_type &get(union_type &u) { return u.storage; } template <typename U, typename... UArgs, typename std::enable_if< std::is_same<U, value_type>::value, int >::type = 0> static value_type &construct( allocator_type const &allocator, size_type const depth, union_type &u, UArgs &&...args ) { assert(depth == Depth), (void) depth; return storage_policy::template construct<value_type>( allocator, u.storage, std::forward<UArgs>(args)... ); } }; // general case template < typename TStoragePolicy, typename TSize, TSize Depth, typename T, typename Head, typename... Tail > struct variadic_union_traits<TStoragePolicy, TSize, Depth, T, Head, Tail...> { static_assert(std::is_integral<TSize>::value, "TSize must be an integral"); static_assert(!std::is_reference<T>::value, "T can't be a reference"); static_assert(!std::is_const<T>::value, "T can't be const"); using size_type = TSize; using value_type = T; using storage_policy = TStoragePolicy; using allocator_type = typename storage_policy::allocator_type; using storage_type = typename storage_policy ::template storage_type<value_type>::type; using head_type = variant_impl::variadic_union_traits< storage_policy, size_type, Depth, T >; using tail_type = variant_impl::variadic_union_traits< storage_policy, size_type, Depth + 1, Head, Tail... >; union union_type { typename head_type::union_type head; typename tail_type::union_type tail; union_type() {} union_type(union_type const &) {} union_type(union_type &&) {} ~union_type() {} }; // calls not dependent on value_type constexpr static size_type end_depth() { return tail_type::end_depth(); } template <template <typename> class... TTransforms> static std::type_info const &type(size_type const depth) { return depth == Depth ? head_type::template type<TTransforms...>(depth) : tail_type::template type<TTransforms...>(depth); } template <typename TVisitor, typename... UArgs> static void visit( size_type const depth, union_type const &u, TVisitor &&visitor, UArgs &&...args ) { if (depth == Depth) { head_type::template visit( depth, u.head, std::forward<TVisitor>(visitor), std::forward<UArgs>(args)... ); } else { tail_type::template visit( depth, u.tail, std::forward<TVisitor>(visitor), std::forward<UArgs>(args)... ); } } template <typename TVisitor, typename... UArgs> static void visit( size_type const depth, union_type &u, TVisitor &&visitor, UArgs &&...args ) { if (depth == Depth) { head_type::template visit( depth, u.head, std::forward<TVisitor>(visitor), std::forward<UArgs>(args)... ); } else { tail_type::template visit( depth, u.tail, std::forward<TVisitor>(visitor), std::forward<UArgs>(args)... ); } } static void allocate( allocator_type const &allocator, size_type const depth, union_type &u ) { if (depth == Depth) { head_type::allocate(allocator, depth, u.head); } else { tail_type::allocate(allocator, depth, u.tail); } } static void deallocate( allocator_type const &allocator, size_type const depth, union_type &u ) { if (depth == Depth) { head_type::deallocate(allocator, depth, u.head); } else { tail_type::deallocate(allocator, depth, u.tail); } } static void copy_construct( allocator_type const &allocator, size_type const depth, union_type const &from, union_type &to ) { if (depth == Depth) { head_type::copy_construct(allocator, depth, from.head, to.head); } else { tail_type::copy_construct(allocator, depth, from.tail, to.tail); } } static void destroy( allocator_type const &allocator, size_type const depth, union_type &u ) { if (depth == Depth) { head_type::destroy(allocator, depth, u.head); } else { tail_type::destroy(allocator, depth, u.tail); } } static void move_over( size_type const depth, union_type &from, union_type &to ) { if (depth == Depth) { head_type::move_over(depth, from.head, to.head); } else { tail_type::move_over(depth, from.tail, to.tail); } } static bool equals( size_type const depth, union_type const &lhs, union_type const &rhs ) { return depth == Depth ? head_type::equals(depth, lhs.head, rhs.head) : tail_type::equals(depth, lhs.tail, rhs.tail); } static bool less_than( size_type const depth, union_type const &lhs, union_type const &rhs ) { return depth == Depth ? head_type::less_than(depth, lhs.head, rhs.head) : tail_type::less_than(depth, lhs.tail, rhs.tail); } // templated calls dependent on value_type // returns end_depth() when U is not found template <typename U> constexpr static size_type get_depth() { return std::is_same<U, value_type>::value ? head_type::template get_depth<U>() : tail_type::template get_depth<U>(); } template <typename U, typename std::enable_if< std::is_same<U, value_type>::value, int >::type = 0> static storage_type const &get(union_type const &u) { return head_type::template get<U>(u.head); } template <typename U, typename std::enable_if< std::is_same<U, value_type>::value, int >::type = 0> static storage_type &get(union_type &u) { return head_type::template get<U>(u.head); } template <typename U, typename std::enable_if< !std::is_same<U, value_type>::value, int >::type = 0> static typename storage_policy::template storage_type<U>::type const &get( union_type const &u ) { return tail_type::template get<U>(u.tail); } template <typename U, typename std::enable_if< !std::is_same<U, value_type>::value, int >::type = 0> static typename storage_policy::template storage_type<U>::type &get( union_type &u ) { return tail_type::template get<U>(u.tail); } template <typename U, typename... UArgs, typename std::enable_if< std::is_same<U, value_type>::value, int >::type = 0> static U &construct( allocator_type const &allocator, size_type const depth, union_type &u, UArgs &&...args ) { return head_type::template construct<U>( allocator, depth, u.head, std::forward<UArgs>(args)... ); } template <typename U, typename... UArgs, typename std::enable_if< !std::is_same<U, value_type>::value, int >::type = 0> static U &construct( allocator_type const &allocator, size_type const depth, union_type &u, UArgs &&...args ) { return tail_type::template construct<U>( allocator, depth, u.tail, std::forward<UArgs>(args)... ); } }; template <template <typename...> class UCondition> class visit_if_visitor { template < bool Condition, typename U, typename UVisitor, typename... UArgs, typename std::enable_if<Condition, int>::type = 0 > bool visit( U &&value, UVisitor &&visitor, UArgs &&...args ) const { visitor(std::forward<U>(value), std::forward<UArgs>(args)...); return true; } template < bool Condition, typename U, typename UVisitor, typename... UArgs, typename std::enable_if<!Condition, int>::type = 0 > bool visit( U &&, UVisitor &&, UArgs &&... ) const { return false; } public: template < typename U, std::size_t Index, typename UTag, typename UVariant, typename UVisitor, typename... UArgs > void operator ()( indexed_type_tag<U, Index>, UTag &&tag, UVariant &&variant, bool &result, UVisitor &&visitor, UArgs &&...args ) { assert(tag == variant.tag()), (void) tag; assert(Index == variant.tag()); using variant_type = typename std::decay<UVariant>::type; static_assert(sizeof(variant_type) >= 0, ""); result = visit<UCondition<U>::value>( variant.variant_type::template get<U>(), std::forward<UVisitor>(visitor), std::forward<UArgs>(args)... ); } }; template <typename T, bool> struct nothrow_cp_ctor { using type = std::is_nothrow_copy_constructible<T>; }; template <typename T> struct nothrow_cp_ctor<T, true> { using type = std::false_type; }; template <typename T, bool> struct nothrow_mv_ctor { using type = std::is_nothrow_move_constructible<T>; }; template <typename T> struct nothrow_mv_ctor<T, true> { using type = std::true_type; }; template <typename T, bool> struct nothrow_cp_assign { using type = std::is_nothrow_copy_assignable<T>; }; template <typename T> struct nothrow_cp_assign<T, true> { using type = std::false_type; }; template <typename T, bool> struct nothrow_mv_assign { using type = std::is_nothrow_move_assignable<T>; }; template <typename T> struct nothrow_mv_assign<T, true> { using type = std::true_type; }; } // namespace variant_impl { } // namespace detail { template <typename, typename...> struct legacy_variant; namespace detail { namespace variant_impl { template <typename> struct is_variant_impl; template <typename UStoragePolicy, typename... UArgs> struct is_variant_impl<legacy_variant<UStoragePolicy, UArgs...>> {}; } // namespace variant_impl { } // namespace detail { template <typename T> using is_variant = is_complete<detail::variant_impl::is_variant_impl<T>>; /** * ################ * # 'START HERE' # * ################ * * This class represents a variant container which can store values of * different types, one at a time. * * The underlying storage used to hold these values can be fully customized * by means of a StoragePolicy. A default implementation for this policy is * provided (fatal::default_storage_policy) which allows the usage of * either automatic or dynamic allocation to store the values (this can be * further customized per type by means of an AllocationPolicy, search for * 'STORAGE POLICY' for more details). * * An Allocator must also be provided in order to handle dynamic allocation. * * @author: Marcelo Juchem <marcelo@fb.com> */ // TODO: ADD MISSING noexcept DECLARATIONS template <typename TStoragePolicy, typename... Args> struct legacy_variant { using storage_policy = TStoragePolicy; using allocator_type = typename storage_policy::allocator_type; using types = type_list<Args...>; private: template <typename UStoragePolicy, typename... UArgs> friend struct legacy_variant; /** * This is the control block used by the variant to hold both the allocator * and the type_tag of the type currently stored in the variant. */ struct control_block { // bits_size is the amount of bits needed to represent the type_tag for // the given types passed as variadic template parameters (Args...) constexpr static std::size_t bits_size() { return most_significant_bit<sizeof...(Args)>::value; } using type_tag = smallest_uint_for_value<sizeof...(Args)>; control_block(allocator_type const &allocator, type_tag storedType): allocator_(allocator), tag_(std::move(storedType)) {} fast_pass<type_tag> storedType() const { return tag_; } void setStoredType(type_tag tag) { tag_ = std::move(tag); } allocator_type const &get_allocator() const { return allocator_; } allocator_type allocator_; type_tag tag_; }; template <typename T> using nothrow_cp_ctor = typename detail::variant_impl::nothrow_cp_ctor< T, storage_policy::template allocate_dynamically<T>::value >::type; template <typename T> using nothrow_mv_ctor = typename detail::variant_impl::nothrow_mv_ctor< T, storage_policy::template allocate_dynamically<T>::value >::type; template <typename T> using nothrow_cp_assign = typename detail::variant_impl::nothrow_cp_assign< T, storage_policy::template allocate_dynamically<T>::value >::type; template <typename T> using nothrow_mv_assign = typename detail::variant_impl::nothrow_mv_assign< T, storage_policy::template allocate_dynamically<T>::value >::type; public: using type_tag = typename control_block::type_tag; using traits = detail::variant_impl::variadic_union_traits< storage_policy, type_tag, std::numeric_limits<type_tag>::min(), typename std::decay<Args>::type... >; using union_type = typename traits::union_type; // returns no_tag() when the given type is not part of the variant template <typename U> constexpr static type_tag tag() { return traits::template get_depth<U>(); } // the tag used when the variant is empty constexpr static type_tag no_tag() { return traits::end_depth(); } // returns true iff this type can be stored in the variant template <typename U> constexpr static bool is_supported() { return tag<U>() != no_tag(); } legacy_variant() noexcept: control_(allocator_type(), no_tag()) {} explicit legacy_variant(allocator_type const &allocator) noexcept: control_(allocator, no_tag()) {} legacy_variant(legacy_variant const &other) noexcept(logical::all<nothrow_cp_ctor<Args>...>::value): control_(copy_impl(other)) { static_assert( storage_policy::is_copyable(), "copy construction disabled by the variant's policy" ); } legacy_variant(legacy_variant &&other) noexcept(logical::all<nothrow_mv_ctor<Args>...>::value): control_(move_impl(std::move(other))) {} template <typename U> explicit legacy_variant(allocator_type const &allocator, U &&value): control_(allocator, no_tag()) { set<typename std::decay<U>::type>(std::forward<U>(value)); } template < typename U, typename = safe_overload<legacy_variant, U>, typename std::enable_if< !std::is_convertible<U&&, allocator_type const &>::value, int >::type = 0 > explicit legacy_variant(U &&value) : control_(allocator_type(), no_tag()) { set(std::forward<U>(value)); } template <typename... UArgs> legacy_variant(legacy_variant<storage_policy, UArgs...> const &other): legacy_variant(other.control_.get_allocator()) { other.visit(copy_variant_visitor<true, false>(), *this); } template <typename UStoragePolicy, typename... UArgs> legacy_variant( allocator_type const &allocator, legacy_variant<UStoragePolicy, UArgs...> const &other ): legacy_variant(allocator) { other.visit(copy_variant_visitor<true, false>(), *this); } ~legacy_variant() { unset_impl(); } /** * The unchecked_get won't perform type checks. The user must ensure the * requested type matches the one stored in the variant. Provided for * performance sensitive scenarios. */ template <typename U> U const &unchecked_get() const & { return storage_policy::template get<U>( traits::template get<U>(union_) ); } template <typename U> U &unchecked_get() & { return storage_policy::template get<U>( traits::template get<U>(union_) ); } template <typename U> U &&unchecked_get() && { return std::move(storage_policy::template get<U>( traits::template get<U>(union_) )); } template <typename U> U const &get() const & { if (tag<U>() != control_.storedType()) { throw std::invalid_argument( "requested type doesn't match the one contained in the variant" ); } return storage_policy::template get<U>( traits::template get<U>(union_) ); } template <typename U> U &get() & { if (tag<U>() != control_.storedType()) { throw std::invalid_argument( "requested type doesn't match the one contained in the variant" ); } return storage_policy::template get<U>( traits::template get<U>(union_) ); } template <typename U> U &&get() && { if (tag<U>() != control_.storedType()) { throw std::invalid_argument( "requested type doesn't match the one contained in the variant" ); } return std::move(storage_policy::template get<U>( traits::template get<U>(union_) )); } template <typename U> U const *try_get() const & { if (tag<U>() != control_.storedType()) { return nullptr; } return std::addressof( storage_policy::template get<U>( traits::template get<U>(union_) ) ); } template <typename U> U *try_get() & { if (tag<U>() != control_.storedType()) { return nullptr; } return std::addressof( storage_policy::template get<U>( traits::template get<U>(union_) ) ); } template <typename U> U *try_get() && = delete; template < template <typename...> class UCondition, typename UVisitor, typename... UArgs > bool visit_if(UVisitor &&visitor, UArgs &&...args) const { bool result = false; return types::template binary_search<index_value_comparer>::exact( control_.storedType(), detail::variant_impl::visit_if_visitor<UCondition>(), *this, result, std::forward<UVisitor>(visitor), std::forward<UArgs>(args)... ) && result; } template < template <typename...> class UCondition, typename UVisitor, typename... UArgs > bool visit_if(UVisitor &&visitor, UArgs &&...args) { bool result = false; return types::template binary_search<index_value_comparer>::exact( control_.storedType(), detail::variant_impl::visit_if_visitor<UCondition>(), *this, result, std::forward<UVisitor>(visitor), std::forward<UArgs>(args)... ) && result; } template <typename TVisitor, typename... UArgs> bool visit(TVisitor &&visitor, UArgs &&...args) const { auto const storedType = control_.storedType(); if (storedType == no_tag()) { return false; } traits::template visit( storedType, union_, std::forward<TVisitor>(visitor), std::forward<UArgs>(args)... ); return true; } template <typename TVisitor, typename... UArgs> bool visit(TVisitor &&visitor, UArgs &&...args) { auto const storedType = control_.storedType(); if (storedType == no_tag()) { return false; } traits::template visit( storedType, union_, std::forward<TVisitor>(visitor), std::forward<UArgs>(args)... ); return true; } template <typename U, typename std::enable_if< is_supported<typename std::decay<U>::type>(), int >::type = 0> bool try_set(U &&value) { *this = std::forward<U>(value); return true; } template <typename U, typename std::enable_if< !is_supported<typename std::decay<U>::type>(), int >::type = 0> bool try_set(U &&) { return false; } template <typename UCallable, typename... UArgs, typename std::enable_if< is_supported< typename std::decay< typename std::result_of<UCallable&(UArgs...)>::type >::type >(), int >::type = 0> bool set_result_of(UCallable &&callable, UArgs &&...args) { *this = callable(std::forward<UArgs>(args)...); return true; } template <typename UCallable, typename... UArgs, typename std::enable_if< !is_supported< typename std::decay< typename std::result_of<UCallable&(UArgs...)>::type >::type >(), int >::type = 0> bool set_result_of(UCallable &&callable, UArgs &&...args) { callable(std::forward<UArgs>(args)...); return false; } template <typename U> U &set(U const &value) & { return set_impl<typename std::decay<U>::type>(value); } template <typename U, typename = enable_when::movable<U>> U &set(U &&value) & { return set_impl<typename std::decay<U>::type>( std::forward<U>(value) ); } template <typename U> U &&set(U const &value) && { return std::move(set_impl<typename std::decay<U>::type>(value)); } template <typename U, typename = enable_when::movable<U>> U &&set(U &&value) && { return std::move(set_impl<typename std::decay<U>::type>( std::forward<U>(value) )); } template <typename U, typename... UArgs> U &emplace(UArgs &&...args) & { return set_impl<typename std::decay<U>::type>( std::forward<UArgs>(args)... ); } template <typename U, typename... UArgs> U &&emplace(UArgs &&...args) && { return std::move(set_impl<typename std::decay<U>::type>( std::forward<UArgs>(args)... )); } void clear() { unset_impl(); } bool empty() const { return control_.storedType() == no_tag(); } void swap(legacy_variant &other) { if (this == std::addressof(other)) { return; } auto const storedType = control_.storedType(); auto const otherStoredType = other.control_.storedType(); union_type tmp; if (storedType != no_tag()) { traits::move_over(storedType, union_, tmp); } if (otherStoredType != no_tag()) { traits::move_over(otherStoredType, other.union_, union_); } if (storedType != no_tag()) { traits::move_over(storedType, tmp, other.union_); } std::swap(control_, other.control_); } type_tag tag() const { return control_.storedType(); } template <typename U> bool is_of() const { return tag() == tag<U>(); } template <typename U> using tag_for = std::integral_constant<type_tag, legacy_variant::tag<U>()>; template <typename UList> using tags_for = typename UList::template transform<tag_for>; template <typename... U> bool is_any_of() const { return tags_for<type_list<U...>>::template binary_search<>::exact( tag(), fn::no_op() ); } template <typename UList> bool is_any_from() const { return tags_for<UList>::template binary_search<>::exact(tag(), fn::no_op()); } // for compatibility with boost::variant typename std::enable_if< traits::end_depth() <= std::numeric_limits<int>::max(), int >::type which() const { return static_cast<int>(tag()); } // for compatibility with boost::variant template <template <typename> class... TTransforms> std::type_info const &type() const { return traits::template type<TTransforms...>(control_.storedType()); } allocator_type get_allocator() const { return control_.get_allocator(); } legacy_variant &operator =(legacy_variant const &other) noexcept( logical::all<std::true_type, nothrow_cp_assign<Args>...>::value ) { static_assert( storage_policy::is_copyable(), "copy assignment disabled by the variant's policy" ); if (this != std::addressof(other)) { unset_impl(); control_ = copy_impl(other); } return *this; } legacy_variant &operator =(legacy_variant &other) noexcept(logical::all<nothrow_cp_assign<Args>...>::value) { return (*this = static_cast<legacy_variant const &>(other)); } legacy_variant &operator =(legacy_variant &&other) noexcept(logical::all<nothrow_mv_assign<Args>...>::value) { if (this != std::addressof(other)) { unset_impl(); control_ = move_impl(std::move(other)); } return *this; } template <typename U> U &operator =(U const &value) & { return set(value); } template < typename U, typename = safe_overload<legacy_variant, U>, typename = enable_when::movable<U> > U &operator =(U &&value) & { return set(std::forward<U>(value)); } template <typename U> U &&operator =(U const &value) && { return std::move(set(value)); } template < typename U, typename = safe_overload<legacy_variant, U>, typename = enable_when::movable<U> > U &&operator =(U &&value) && { return std::move(set(std::forward<U>(value))); } template <typename UStoragePolicy, typename... UArgs> legacy_variant &operator =( legacy_variant<UStoragePolicy, UArgs...> const &other ) { if (!other.visit(copy_variant_visitor<false, true>(), *this)) { clear(); } return *this; } template <typename UStoragePolicy, typename... UArgs> legacy_variant &operator =(legacy_variant<UStoragePolicy, UArgs...> &other) { if (!other.visit(copy_variant_visitor<false, true>(), *this)) { clear(); } return *this; } template <typename... UArgs> legacy_variant &operator =(legacy_variant<storage_policy, UArgs...> &&other) { if (control_.get_allocator() == other.control_.get_allocator()) { if (!other.visit(copy_variant_visitor<false, true>(), *this)) { clear(); } other.clear(); } else { // can't move when different storage policies // or different allocators are employed if (storage_policy::is_copyable()) { if (!other.visit(copy_variant_visitor<false, true>(), *this)) { clear(); } other.clear(); } else { throw std::invalid_argument( "can't move values allocated by different allocators" " and storage policy doesn't allow copies" ); } } return *this; } template <typename UStoragePolicy, typename... UArgs> legacy_variant &operator =(legacy_variant<UStoragePolicy, UArgs...> &&other) { if (!other.visit(copy_variant_visitor<false, true>(), *this)) { clear(); } other.clear(); return *this; } bool operator ==(legacy_variant const &other) const { auto const storedType = control_.storedType(); if (storedType != other.control_.storedType()) { return false; } return traits::equals(storedType, union_, other.union_); } bool operator <(legacy_variant const &other) const { auto const storedType = control_.storedType(); auto const otherStoredType = other.control_.storedType(); if (storedType != otherStoredType) { return storedType < otherStoredType; } return traits::less_than(storedType, union_, other.union_); } bool operator !=(legacy_variant const &other) const { return !(*this == other); } bool operator >(legacy_variant const &other) const { return other < *this; } bool operator <=(legacy_variant const &other) const { return !(other < *this); } bool operator >=(legacy_variant const &other) const { return !(*this < other); } private: union_type union_; control_block control_; template <typename U, typename... UArgs> U &set_impl(UArgs &&...args) { auto const storedType = control_.storedType(); auto const &allocator = control_.get_allocator(); static_assert((is_supported<U>()), "can't set an unsupported type"); if (storedType == tag<U>()) { assert(storedType != no_tag()); auto &storage = traits::template get<U>(union_); storage_policy::template destroy<U>(allocator, storage); try { return storage_policy::template construct<U>( allocator, storage, std::forward<UArgs>(args)... ); } catch(...) { control_.setStoredType(no_tag()); storage_policy::template deallocate<U>(allocator, storage); throw; } } unset_impl(); traits::allocate(allocator, tag<U>(), union_); try { auto &result = traits::template construct<U>( allocator, tag<U>(), union_, std::forward<UArgs>(args)... ); control_.setStoredType(tag<U>()); return result; } catch(...) { traits::deallocate(allocator, tag<U>(), union_); throw; } } void unset_impl() { auto const storedType = control_.storedType(); auto const &allocator = control_.get_allocator(); if (storedType == no_tag()) { return; } traits::destroy(allocator, storedType, union_); traits::deallocate(allocator, storedType, union_); control_.setStoredType(no_tag()); } // assumes already unset control_block copy_impl(legacy_variant const &other) { auto const otherStoredType = other.control_.storedType(); auto otherAllocator = other.control_.get_allocator(); if (otherStoredType == no_tag()) { return other.control_; } traits::allocate(otherAllocator, otherStoredType, union_); try { traits::copy_construct( otherAllocator, otherStoredType, other.union_, union_ ); } catch(...) { traits::deallocate(otherAllocator, otherStoredType, union_); throw; } return other.control_; } // assumes already unset control_block move_impl(legacy_variant &&other) { auto const otherStoredType = other.control_.storedType(); if (otherStoredType != no_tag()) { traits::move_over(otherStoredType, other.union_, union_); } control_block control( other.control_.get_allocator(), other.control_.storedType() ); other.control_.setStoredType(no_tag()); return control; } template <bool ThrowIfUnsupported, bool ClearIfUnsupported> class copy_variant_visitor { template <typename U, bool> struct setter { static void set(legacy_variant &v, U const &value) { v.set(value); } }; template <typename U> struct setter<U, false> { static void set(legacy_variant &v, U const &) { if (ClearIfUnsupported) { v.clear(); } if (ThrowIfUnsupported) { throw std::invalid_argument("type not supported"); } } }; public: template <typename U> void operator ()(U const &value, legacy_variant &v) { static_assert( storage_policy::is_copyable(), "copy disabled by the variant's policy" ); using type = typename std::decay<U>::type; setter<type, legacy_variant::template is_supported<type>()>::set( v, value ); } }; }; template <typename TStoragePolicy, typename... Args> using variant = legacy_variant<TStoragePolicy, Args...>; template <typename... Args> using legacy_default_variant = legacy_variant< legacy_storage_policy<>, Args... >; template <typename... Args> using default_variant = legacy_default_variant<Args...>; template <typename... Args> using auto_variant = variant< default_storage_policy< std::allocator<void>, automatic_allocation_policy >, Args... >; template <typename Allocator, typename... Args> using dynamic_variant = variant< default_storage_policy<Allocator, dynamic_allocation_policy>, Args... >; template <typename... Args> using default_dynamic_variant = dynamic_variant<std::allocator<void>, Args...>; /** * Wraps non-derived visitors that return a value on operator() * * @author: Marcelo Juchem <marcelo@fb.com> */ template <typename TVisitor, typename TResult> struct visitor_wrapper { using visitor_type = TVisitor; using result_type = TResult; explicit visitor_wrapper(visitor_type &visitor): visitor_(visitor) {} template <typename... UArgs> void operator()(UArgs &&...args) { result_.emplace(visitor_(std::forward<UArgs>(args)...)); } bool has_value() const { return !result_.empty(); } fast_pass<result_type> value() const { return *result_; } result_type &value() { return result_.ref(); } private: visitor_type &visitor_; optional<result_type> result_; }; /** * A handy function to build visitor_wrapper instances given a visitor * whose operator() returns a value * * @author: Marcelo Juchem <marcelo@fb.com> */ template <typename result_type, typename visitor_type> visitor_wrapper<visitor_type const, result_type> wrap_visitor( visitor_type const &visitor ) { return visitor_wrapper<visitor_type const, result_type>(visitor); } template <typename result_type, typename visitor_type> visitor_wrapper<visitor_type, result_type> wrap_visitor( visitor_type &visitor ) { return visitor_wrapper<visitor_type, result_type>(visitor); } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function throws std::logic_error. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type visit( variant<TStoragePolicy, Args...> const &variant, visitor_type &&visitor, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); if (!wrapper.has_value()) { throw std::logic_error("there's no value returned by the visitor"); } return std::move(wrapper.value()); } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function throws std::logic_error. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type visit( variant<TStoragePolicy, Args...> &variant, visitor_type &&visitor, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); if (!wrapper.has_value()) { throw std::logic_error("there's no value returned by the visitor"); } return std::move(wrapper.value()); } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function throws std::logic_error. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type visit( variant<TStoragePolicy, Args...> &&variant, visitor_type &&visitor, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); if (!wrapper.has_value()) { throw std::logic_error("there's no value returned by the visitor"); } return std::move(wrapper.value()); } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function throws std::logic_error. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type legacy_visit( variant<TStoragePolicy, Args...> const &variant, visitor_type &&visitor, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); if (!wrapper.has_value()) { throw std::logic_error("there's no value returned by the visitor"); } return std::move(wrapper.value()); } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function throws std::logic_error. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type legacy_visit( variant<TStoragePolicy, Args...> &variant, visitor_type &&visitor, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); if (!wrapper.has_value()) { throw std::logic_error("there's no value returned by the visitor"); } return std::move(wrapper.value()); } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function throws std::logic_error. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type legacy_visit( variant<TStoragePolicy, Args...> &&variant, visitor_type &&visitor, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); if (!wrapper.has_value()) { throw std::logic_error("there's no value returned by the visitor"); } return std::move(wrapper.value()); } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function returns `defaultValue`. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type visit_def( variant<TStoragePolicy, Args...> const &variant, visitor_type &&visitor, result_type defaultValue, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); return wrapper.has_value() ? std::move(wrapper.value()) : defaultValue; } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function returns `defaultValue`. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type visit_def( variant<TStoragePolicy, Args...> &variant, visitor_type &&visitor, result_type defaultValue, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); return wrapper.has_value() ? std::move(wrapper.value()) : defaultValue; } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function returns `defaultValue`. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type visit_def( variant<TStoragePolicy, Args...> &&variant, visitor_type &&visitor, result_type defaultValue, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); return wrapper.has_value() ? std::move(wrapper.value()) : defaultValue; } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function returns `defaultValue`. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type legacy_visit_def( variant<TStoragePolicy, Args...> const &variant, visitor_type &&visitor, result_type defaultValue, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); return wrapper.has_value() ? std::move(wrapper.value()) : defaultValue; } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function returns `defaultValue`. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type legacy_visit_def( variant<TStoragePolicy, Args...> &variant, visitor_type &&visitor, result_type defaultValue, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); return wrapper.has_value() ? std::move(wrapper.value()) : defaultValue; } /** * Convenience function to visit a variant using a visitor that returns values. * * If the variant holds no values (the visitor is not actually called), this * function returns `defaultValue`. * * @author: Marcelo Juchem <marcelo@fb.com> */ template < typename result_type, typename visitor_type, typename TStoragePolicy, typename... Args, typename... UArgs > result_type legacy_visit_def( variant<TStoragePolicy, Args...> &&variant, visitor_type &&visitor, result_type defaultValue, UArgs &&...args ) { auto wrapper = wrap_visitor<result_type>(visitor); variant.visit(wrapper, std::forward<UArgs>(args)...); return wrapper.has_value() ? std::move(wrapper.value()) : defaultValue; } } // namespace fatal { namespace std { template <typename TStoragePolicy, typename... Args> struct hash<fatal::variant<TStoragePolicy, Args...>> { using result_type = std::size_t; result_type operator ()( fatal::variant<TStoragePolicy, Args...> const &v ) const { hash_visitor visitor(std::hash<decltype(v.tag())>()(v.tag())); v.visit(visitor); return visitor.result; } private: struct hash_visitor { explicit hash_visitor(result_type seed): result(std::move(seed)) {} template <typename U> void operator ()(U const &value) { result ^= std::hash<typename std::decay<U>::type>()(value); } result_type result; }; }; } // namespace std { FATAL_DIAGNOSTIC_POP #endif // FATAL_INCLUDE_fatal_container_legacy_variant_h