| #ifndef ANDROID_PDX_RPC_VARIANT_H_ |
| #define ANDROID_PDX_RPC_VARIANT_H_ |
| |
| #include <cstdint> |
| #include <tuple> |
| #include <type_traits> |
| |
| namespace android { |
| namespace pdx { |
| namespace rpc { |
| |
| // Type tag denoting an empty variant. |
| struct EmptyVariant {}; |
| |
| namespace detail { |
| |
| // Type for matching tagged overloads. |
| template <typename T> |
| struct TypeTag {}; |
| |
| // Determines the type of the I-th element of Types.... |
| template <std::size_t I, typename... Types> |
| using TypeForIndex = std::tuple_element_t<I, std::tuple<Types...>>; |
| |
| // Determines the type tag for the I-th element of Types.... |
| template <std::size_t I, typename... Types> |
| using TypeTagForIndex = TypeTag<TypeForIndex<I, Types...>>; |
| |
| // Similar to std::is_constructible except that it evaluates to false for bool |
| // construction from pointer types: this helps prevent subtle to bugs caused by |
| // assigning values that decay to pointers to Variants with bool elements. |
| // |
| // Here is an example of the problematic situation this trait avoids: |
| // |
| // Variant<int, bool> v; |
| // const int array[3] = {1, 2, 3}; |
| // v = array; // This is allowed by regular std::is_constructible. |
| // |
| template <typename...> |
| struct IsConstructible; |
| template <typename T, typename U> |
| struct IsConstructible<T, U> |
| : std::integral_constant<bool, |
| std::is_constructible<T, U>::value && |
| !(std::is_same<std::decay_t<T>, bool>::value && |
| std::is_pointer<std::decay_t<U>>::value)> {}; |
| template <typename T, typename... Args> |
| struct IsConstructible<T, Args...> : std::is_constructible<T, Args...> {}; |
| |
| // Enable if T(Args...) is well formed. |
| template <typename R, typename T, typename... Args> |
| using EnableIfConstructible = |
| typename std::enable_if<IsConstructible<T, Args...>::value, R>::type; |
| // Enable if T(Args...) is not well formed. |
| template <typename R, typename T, typename... Args> |
| using EnableIfNotConstructible = |
| typename std::enable_if<!IsConstructible<T, Args...>::value, R>::type; |
| |
| // Determines whether T is an element of Types...; |
| template <typename... Types> |
| struct HasType : std::false_type {}; |
| template <typename T, typename U> |
| struct HasType<T, U> : std::is_same<std::decay_t<T>, std::decay_t<U>> {}; |
| template <typename T, typename First, typename... Rest> |
| struct HasType<T, First, Rest...> |
| : std::integral_constant<bool, HasType<T, First>::value || |
| HasType<T, Rest...>::value> {}; |
| |
| // Defines set operations on a set of Types... |
| template <typename... Types> |
| struct Set { |
| // Default specialization catches the empty set, which is always a subset. |
| template <typename...> |
| struct IsSubset : std::true_type {}; |
| template <typename T> |
| struct IsSubset<T> : HasType<T, Types...> {}; |
| template <typename First, typename... Rest> |
| struct IsSubset<First, Rest...> |
| : std::integral_constant<bool, IsSubset<First>::value && |
| IsSubset<Rest...>::value> {}; |
| }; |
| |
| // Determines the number of elements of Types... that are constructible from |
| // From. |
| template <typename... Types> |
| struct ConstructibleCount; |
| template <typename From, typename To> |
| struct ConstructibleCount<From, To> |
| : std::integral_constant<std::size_t, IsConstructible<To, From>::value> {}; |
| template <typename From, typename First, typename... Rest> |
| struct ConstructibleCount<From, First, Rest...> |
| : std::integral_constant<std::size_t, |
| IsConstructible<First, From>::value + |
| ConstructibleCount<From, Rest...>::value> {}; |
| |
| // Enable if T is an element of Types... |
| template <typename R, typename T, typename... Types> |
| using EnableIfElement = |
| typename std::enable_if<HasType<T, Types...>::value, R>::type; |
| // Enable if T is not an element of Types... |
| template <typename R, typename T, typename... Types> |
| using EnableIfNotElement = |
| typename std::enable_if<!HasType<T, Types...>::value, R>::type; |
| |
| // Enable if T is convertible to an element of Types... T is considered |
| // convertible IIF a single element of Types... is assignable from T and T is |
| // not a direct element of Types... |
| template <typename R, typename T, typename... Types> |
| using EnableIfConvertible = |
| typename std::enable_if<!HasType<T, Types...>::value && |
| ConstructibleCount<T, Types...>::value == 1, |
| R>::type; |
| |
| // Enable if T is assignable to an element of Types... T is considered |
| // assignable IFF a single element of Types... is constructible from T or T is a |
| // direct element of Types.... Note that T is REQUIRED to be an element of |
| // Types... when multiple elements are constructible from T to prevent ambiguity |
| // in conversion. |
| template <typename R, typename T, typename... Types> |
| using EnableIfAssignable = |
| typename std::enable_if<HasType<T, Types...>::value || |
| ConstructibleCount<T, Types...>::value == 1, |
| R>::type; |
| |
| // Selects a type for SFINAE constructor selection. |
| template <bool CondA, typename SelectA, typename SelectB> |
| using Select = std::conditional_t<CondA, SelectA, SelectB>; |
| |
| // Recursive union type. |
| template <typename... Types> |
| union Union; |
| |
| // Specialization handling a singular type, terminating template recursion. |
| template <typename Type> |
| union Union<Type> { |
| Union() {} |
| ~Union() {} |
| |
| template <typename T> |
| Union(std::int32_t index, std::int32_t* index_out, TypeTag<Type>, T&& value) |
| : first_(std::forward<T>(value)) { |
| *index_out = index; |
| } |
| template <typename T, typename = EnableIfAssignable<void, T, Type>> |
| Union(std::int32_t index, std::int32_t* index_out, T&& value) |
| : first_(std::forward<T>(value)) { |
| *index_out = index; |
| } |
| Union(const Union& other, std::int32_t index) { |
| if (index == 0) |
| new (&first_) Type(other.first_); |
| } |
| Union(Union&& other, std::int32_t index) { |
| if (index == 0) |
| new (&first_) Type(std::move(other.first_)); |
| } |
| Union(const Union&) = delete; |
| Union(Union&&) = delete; |
| void operator=(const Union&) = delete; |
| void operator=(Union&&) = delete; |
| |
| Type& get(TypeTag<Type>) { return first_; } |
| const Type& get(TypeTag<Type>) const { return first_; } |
| EmptyVariant get(TypeTag<EmptyVariant>) const { return {}; } |
| constexpr std::int32_t index(TypeTag<Type>) const { return 0; } |
| |
| template <typename... Args> |
| std::int32_t Construct(TypeTag<Type>, Args&&... args) { |
| new (&first_) Type(std::forward<Args>(args)...); |
| return 0; |
| } |
| template <typename... Args> |
| EnableIfConstructible<std::int32_t, Type, Args...> Construct(Args&&... args) { |
| new (&first_) Type(std::forward<Args>(args)...); |
| return 0; |
| } |
| |
| void Destruct(std::int32_t target_index) { |
| if (target_index == index(TypeTag<Type>{})) { |
| (&get(TypeTag<Type>{}))->~Type(); |
| } |
| } |
| |
| template <typename T> |
| bool Assign(TypeTag<Type>, std::int32_t target_index, T&& value) { |
| if (target_index == 0) { |
| first_ = std::forward<T>(value); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| template <typename T> |
| EnableIfConstructible<bool, Type, T> Assign(std::int32_t target_index, |
| T&& value) { |
| if (target_index == 0) { |
| first_ = std::forward<T>(value); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| template <typename T> |
| EnableIfNotConstructible<bool, Type, T> Assign(std::int32_t /*target_index*/, |
| T&& /*value*/) { |
| return false; |
| } |
| |
| template <typename Op> |
| decltype(auto) Visit(std::int32_t target_index, Op&& op) { |
| if (target_index == index(TypeTag<Type>{})) |
| return std::forward<Op>(op)(get(TypeTag<Type>{})); |
| else |
| return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{})); |
| } |
| template <typename Op> |
| decltype(auto) Visit(std::int32_t target_index, Op&& op) const { |
| if (target_index == index(TypeTag<Type>{})) |
| return std::forward<Op>(op)(get(TypeTag<Type>{})); |
| else |
| return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{})); |
| } |
| |
| template <typename... Args> |
| bool Become(std::int32_t target_index, Args&&... args) { |
| if (target_index == index(TypeTag<Type>{})) { |
| Construct(TypeTag<Type>{}, std::forward<Args>(args)...); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private: |
| Type first_; |
| }; |
| |
| // Specialization that recursively unions types from the paramater pack. |
| template <typename First, typename... Rest> |
| union Union<First, Rest...> { |
| Union() {} |
| ~Union() {} |
| |
| template <typename T> |
| Union(std::int32_t index, std::int32_t* index_out, TypeTag<First>, T&& value) |
| : first_(std::forward<T>(value)) { |
| *index_out = index; |
| } |
| template <typename T, typename U> |
| Union(std::int32_t index, std::int32_t* index_out, TypeTag<T>, U&& value) |
| : rest_(index + 1, index_out, TypeTag<T>{}, std::forward<U>(value)) {} |
| Union(const Union& other, std::int32_t index) { |
| if (index == 0) |
| new (&first_) First(other.first_); |
| else |
| new (&rest_) Union<Rest...>(other.rest_, index - 1); |
| } |
| Union(Union&& other, std::int32_t index) { |
| if (index == 0) |
| new (&first_) First(std::move(other.first_)); |
| else |
| new (&rest_) Union<Rest...>(std::move(other.rest_), index - 1); |
| } |
| Union(const Union&) = delete; |
| Union(Union&&) = delete; |
| void operator=(const Union&) = delete; |
| void operator=(Union&&) = delete; |
| |
| struct FirstType {}; |
| struct RestType {}; |
| template <typename T> |
| using SelectConstructor = |
| Select<ConstructibleCount<T, First>::value == 1, FirstType, RestType>; |
| |
| template <typename T> |
| Union(std::int32_t index, std::int32_t* index_out, T&& value) |
| : Union(index, index_out, std::forward<T>(value), |
| SelectConstructor<T>{}) {} |
| |
| template <typename T> |
| Union(std::int32_t index, std::int32_t* index_out, T&& value, FirstType) |
| : first_(std::forward<T>(value)) { |
| *index_out = index; |
| } |
| template <typename T> |
| Union(std::int32_t index, std::int32_t* index_out, T&& value, RestType) |
| : rest_(index + 1, index_out, std::forward<T>(value)) {} |
| |
| First& get(TypeTag<First>) { return first_; } |
| const First& get(TypeTag<First>) const { return first_; } |
| constexpr std::int32_t index(TypeTag<First>) const { return 0; } |
| |
| template <typename T> |
| T& get(TypeTag<T>) { |
| return rest_.template get(TypeTag<T>{}); |
| } |
| template <typename T> |
| const T& get(TypeTag<T>) const { |
| return rest_.template get(TypeTag<T>{}); |
| } |
| template <typename T> |
| constexpr std::int32_t index(TypeTag<T>) const { |
| return 1 + rest_.template index(TypeTag<T>{}); |
| } |
| |
| template <typename... Args> |
| std::int32_t Construct(TypeTag<First>, Args&&... args) { |
| new (&first_) First(std::forward<Args>(args)...); |
| return 0; |
| } |
| template <typename T, typename... Args> |
| std::int32_t Construct(TypeTag<T>, Args&&... args) { |
| return 1 + |
| rest_.template Construct(TypeTag<T>{}, std::forward<Args>(args)...); |
| } |
| |
| template <typename... Args> |
| EnableIfConstructible<std::int32_t, First, Args...> Construct( |
| Args&&... args) { |
| new (&first_) First(std::forward<Args>(args)...); |
| return 0; |
| } |
| template <typename... Args> |
| EnableIfNotConstructible<std::int32_t, First, Args...> Construct( |
| Args&&... args) { |
| return 1 + rest_.template Construct(std::forward<Args>(args)...); |
| } |
| |
| void Destruct(std::int32_t target_index) { |
| if (target_index == index(TypeTag<First>{})) { |
| (get(TypeTag<First>{})).~First(); |
| } else { |
| rest_.Destruct(target_index - 1); |
| } |
| } |
| |
| template <typename T> |
| bool Assign(TypeTag<First>, std::int32_t target_index, T&& value) { |
| if (target_index == 0) { |
| first_ = std::forward<T>(value); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| template <typename T, typename U> |
| bool Assign(TypeTag<T>, std::int32_t target_index, U&& value) { |
| return rest_.Assign(TypeTag<T>{}, target_index - 1, std::forward<U>(value)); |
| } |
| template <typename T> |
| EnableIfConstructible<bool, First, T> Assign(std::int32_t target_index, |
| T&& value) { |
| if (target_index == 0) { |
| first_ = std::forward<T>(value); |
| return true; |
| } else { |
| return rest_.Assign(target_index - 1, std::forward<T>(value)); |
| } |
| } |
| template <typename T> |
| EnableIfNotConstructible<bool, First, T> Assign(std::int32_t target_index, |
| T&& value) { |
| return rest_.Assign(target_index - 1, std::forward<T>(value)); |
| } |
| |
| // Recursively traverses the union and calls Op on the active value when the |
| // active type is found. If the union is empty Op is called on EmptyVariant. |
| // TODO(eieio): This could be refactored into an array or jump table. It's |
| // unclear whether this would be more efficient for practical variant arity. |
| template <typename Op> |
| decltype(auto) Visit(std::int32_t target_index, Op&& op) { |
| if (target_index == index(TypeTag<First>{})) |
| return std::forward<Op>(op)(get(TypeTag<First>{})); |
| else |
| return rest_.Visit(target_index - 1, std::forward<Op>(op)); |
| } |
| template <typename Op> |
| decltype(auto) Visit(std::int32_t target_index, Op&& op) const { |
| if (target_index == index(TypeTag<First>{})) |
| return std::forward<Op>(op)(get(TypeTag<First>{})); |
| else |
| return rest_.Visit(target_index - 1, std::forward<Op>(op)); |
| } |
| |
| template <typename... Args> |
| bool Become(std::int32_t target_index, Args&&... args) { |
| if (target_index == index(TypeTag<First>{})) { |
| Construct(TypeTag<First>{}, std::forward<Args>(args)...); |
| return true; |
| } else { |
| return rest_.Become(target_index - 1, std::forward<Args>(args)...); |
| } |
| } |
| |
| private: |
| First first_; |
| Union<Rest...> rest_; |
| }; |
| |
| } // namespace detail |
| |
| // Variant is a type safe union that can store values of any of its element |
| // types. A Variant is different than std::tuple in that it only stores one type |
| // at a time or a special empty type. Variants are always default constructible |
| // to empty, even when none of the element types are default constructible. |
| template <typename... Types> |
| class Variant { |
| private: |
| // Convenience types. |
| template <typename T> |
| using TypeTag = detail::TypeTag<T>; |
| template <typename T> |
| using DecayedTypeTag = TypeTag<std::decay_t<T>>; |
| template <std::size_t I> |
| using TypeForIndex = detail::TypeForIndex<I, Types...>; |
| template <std::size_t I> |
| using TypeTagForIndex = detail::TypeTagForIndex<I, Types...>; |
| template <typename T> |
| using HasType = detail::HasType<T, Types...>; |
| template <typename R, typename T> |
| using EnableIfElement = detail::EnableIfElement<R, T, Types...>; |
| template <typename R, typename T> |
| using EnableIfConvertible = detail::EnableIfConvertible<R, T, Types...>; |
| template <typename R, typename T> |
| using EnableIfAssignable = detail::EnableIfAssignable<R, T, Types...>; |
| |
| struct Direct {}; |
| struct Convert {}; |
| template <typename T> |
| using SelectConstructor = detail::Select<HasType<T>::value, Direct, Convert>; |
| |
| // Constructs by type tag when T is an direct element of Types... |
| template <typename T> |
| explicit Variant(T&& value, Direct) |
| : value_(0, &index_, DecayedTypeTag<T>{}, std::forward<T>(value)) {} |
| // Conversion constructor when T is not a direct element of Types... |
| template <typename T> |
| explicit Variant(T&& value, Convert) |
| : value_(0, &index_, std::forward<T>(value)) {} |
| |
| public: |
| // Variants are default construcible, regardless of whether the elements are |
| // default constructible. Default consruction yields an empty Variant. |
| Variant() {} |
| explicit Variant(EmptyVariant) {} |
| ~Variant() { Destruct(); } |
| |
| Variant(const Variant& other) |
| : index_{other.index_}, value_{other.value_, other.index_} {} |
| Variant(Variant&& other) |
| : index_{other.index_}, value_{std::move(other.value_), other.index_} {} |
| |
| // Copy and move construction from Variant types. Each element of OtherTypes |
| // must be convertible to an element of Types. |
| template <typename... OtherTypes> |
| explicit Variant(const Variant<OtherTypes...>& other) { |
| other.Visit([this](const auto& value) { Construct(value); }); |
| } |
| template <typename... OtherTypes> |
| explicit Variant(Variant<OtherTypes...>&& other) { |
| other.Visit([this](auto&& value) { Construct(std::move(value)); }); |
| } |
| |
| Variant& operator=(const Variant& other) { |
| other.Visit([this](const auto& value) { *this = value; }); |
| return *this; |
| } |
| Variant& operator=(Variant&& other) { |
| other.Visit([this](auto&& value) { *this = std::move(value); }); |
| return *this; |
| } |
| |
| // Construction from non-Variant types. |
| template <typename T, typename = EnableIfAssignable<void, T>> |
| explicit Variant(T&& value) |
| : Variant(std::forward<T>(value), SelectConstructor<T>{}) {} |
| |
| // Performs assignment from type T belonging to Types. This overload takes |
| // priority to prevent implicit conversion in cases where T is implicitly |
| // convertible to multiple elements of Types. |
| template <typename T> |
| EnableIfElement<Variant&, T> operator=(T&& value) { |
| Assign(DecayedTypeTag<T>{}, std::forward<T>(value)); |
| return *this; |
| } |
| |
| // Performs assignment from type T not belonging to Types. This overload |
| // matches in cases where conversion is the only viable option. |
| template <typename T> |
| EnableIfConvertible<Variant&, T> operator=(T&& value) { |
| Assign(std::forward<T>(value)); |
| return *this; |
| } |
| |
| // Handles assignment from the empty type. This overload supports assignment |
| // in visitors using generic lambdas. |
| Variant& operator=(EmptyVariant) { |
| Destruct(); |
| return *this; |
| } |
| |
| // Assignment from Variant types. Each element of OtherTypes must be |
| // convertible to an element of Types. Forwards through non-Variant assignment |
| // operators to apply conversion checks. |
| template <typename... OtherTypes> |
| Variant& operator=(const Variant<OtherTypes...>& other) { |
| other.Visit([this](const auto& value) { *this = value; }); |
| return *this; |
| } |
| template <typename... OtherTypes> |
| Variant& operator=(Variant<OtherTypes...>&& other) { |
| other.Visit([this](auto&& value) { *this = std::move(value); }); |
| return *this; |
| } |
| |
| // Becomes the target type, constructing a new element from the given |
| // arguments if necessary. No action is taken if the active element is already |
| // the target type. Otherwise the active element is destroyed and replaced by |
| // constructing an element of the new type using |Args|. An invalid target |
| // type index results in an empty Variant. |
| template <typename... Args> |
| void Become(std::int32_t target_index, Args&&... args) { |
| if (target_index != index()) { |
| Destruct(); |
| index_ = value_.Become(target_index, std::forward<Args>(args)...) |
| ? target_index |
| : kEmptyIndex; |
| } |
| } |
| |
| // Invokes |Op| on the active element. If the Variant is empty |Op| is invoked |
| // on EmptyVariant. |
| template <typename Op> |
| decltype(auto) Visit(Op&& op) { |
| return value_.Visit(index_, std::forward<Op>(op)); |
| } |
| template <typename Op> |
| decltype(auto) Visit(Op&& op) const { |
| return value_.Visit(index_, std::forward<Op>(op)); |
| } |
| |
| // Index returned when the Variant is empty. |
| enum : std::int32_t { kEmptyIndex = -1 }; |
| |
| // Returns the index of the given type. |
| template <typename T> |
| constexpr std::int32_t index_of() const { |
| static_assert(HasType<T>::value, "T is not an element type of Variant."); |
| return value_.template index(DecayedTypeTag<T>{}); |
| } |
| |
| // Returns the index of the active type. If the Variant is empty -1 is |
| // returned. |
| std::int32_t index() const { return index_; } |
| |
| // Returns true if the given type is active, false otherwise. |
| template <typename T> |
| bool is() const { |
| static_assert(HasType<T>::value, "T is not an element type of Variant."); |
| return index() == index_of<T>(); |
| } |
| |
| // Returns true if the Variant is empty, false otherwise. |
| bool empty() const { return index() == kEmptyIndex; } |
| |
| // Element accessors. Returns a pointer to the active value if the given |
| // type/index is active, otherwise nullptr is returned. |
| template <typename T> |
| T* get() { |
| if (is<T>()) |
| return &value_.template get(DecayedTypeTag<T>{}); |
| else |
| return nullptr; |
| } |
| template <typename T> |
| const T* get() const { |
| if (is<T>()) |
| return &value_.template get(DecayedTypeTag<T>{}); |
| else |
| return nullptr; |
| } |
| template <std::size_t I> |
| TypeForIndex<I>* get() { |
| if (is<TypeForIndex<I>>()) |
| return &value_.template get(TypeTagForIndex<I>{}); |
| else |
| return nullptr; |
| } |
| template <std::size_t I> |
| const TypeForIndex<I>* get() const { |
| if (is<TypeForIndex<I>>()) |
| return &value_.template get(TypeTagForIndex<I>{}); |
| else |
| return nullptr; |
| } |
| |
| private: |
| std::int32_t index_ = kEmptyIndex; |
| detail::Union<std::decay_t<Types>...> value_; |
| |
| // Constructs an element from the given arguments and sets the Variant to the |
| // resulting type. |
| template <typename... Args> |
| void Construct(Args&&... args) { |
| index_ = value_.template Construct(std::forward<Args>(args)...); |
| } |
| void Construct(EmptyVariant) {} |
| |
| // Destroys the active element of the Variant. |
| void Destruct() { |
| value_.Destruct(index_); |
| index_ = kEmptyIndex; |
| } |
| |
| // Assigns the Variant when non-empty and the current type matches the target |
| // type, otherwise destroys the current value and constructs a element of the |
| // new type. Tagged assignment is used when T is an element of the Variant to |
| // prevent implicit conversion in cases where T is implicitly convertible to |
| // multiple element types. |
| template <typename T, typename U> |
| void Assign(TypeTag<T>, U&& value) { |
| if (!value_.template Assign(TypeTag<T>{}, index_, std::forward<U>(value))) { |
| Destruct(); |
| Construct(TypeTag<T>{}, std::forward<U>(value)); |
| } |
| } |
| template <typename T> |
| void Assign(T&& value) { |
| if (!value_.template Assign(index_, std::forward<T>(value))) { |
| Destruct(); |
| Construct(std::forward<T>(value)); |
| } |
| } |
| }; |
| |
| // Utility type to extract/convert values from a variant. This class simplifies |
| // conditional logic to get/move/swap/action values from a variant when one or |
| // more elements are compatible with the destination type. |
| // |
| // Example: |
| // Variant<int, bool, std::string> v(10); |
| // bool bool_value; |
| // if (IfAnyOf<int, bool>::Get(v, &bool_value)) { |
| // DoSomething(bool_value); |
| // } else { |
| // HandleInvalidType(); |
| // } |
| // IfAnyOf<int>::Call(v, [](const auto& value) { DoSomething(value); }); |
| // |
| template <typename... ValidTypes> |
| struct IfAnyOf { |
| // Calls Op on the underlying value of the variant and returns true when the |
| // variant is a valid type, otherwise does nothing and returns false. |
| template <typename Op, typename... Types> |
| static bool Call(Variant<Types...>* variant, Op&& op) { |
| static_assert( |
| detail::Set<Types...>::template IsSubset<ValidTypes...>::value, |
| "ValidTypes may only contain element types from the Variant."); |
| return variant->Visit(CallOp<Op>{std::forward<Op>(op)}); |
| } |
| template <typename Op, typename... Types> |
| static bool Call(const Variant<Types...>* variant, Op&& op) { |
| static_assert( |
| detail::Set<Types...>::template IsSubset<ValidTypes...>::value, |
| "ValidTypes may only contain element types from the Variant."); |
| return variant->Visit(CallOp<Op>{std::forward<Op>(op)}); |
| } |
| |
| // Gets/converts the underlying value of the variant to type T and returns |
| // true when the variant is a valid type, otherwise does nothing and returns |
| // false. |
| template <typename T, typename... Types> |
| static bool Get(const Variant<Types...>* variant, T* value_out) { |
| return Call(variant, |
| [value_out](const auto& value) { *value_out = value; }); |
| } |
| |
| // Moves the underlying value of the variant and returns true when the variant |
| // is a valid type, otherwise does nothing and returns false. |
| template <typename T, typename... Types> |
| static bool Take(Variant<Types...>* variant, T* value_out) { |
| return Call(variant, |
| [value_out](auto&& value) { *value_out = std::move(value); }); |
| } |
| |
| // Swaps the underlying value of the variant with |*value_out| and returns |
| // true when the variant is a valid type, otherwise does nothing and returns |
| // false. |
| template <typename T, typename... Types> |
| static bool Swap(Variant<Types...>* variant, T* value_out) { |
| return Call(variant, |
| [value_out](auto&& value) { std::swap(*value_out, value); }); |
| } |
| |
| private: |
| template <typename Op> |
| struct CallOp { |
| Op&& op; |
| template <typename U> |
| detail::EnableIfNotElement<bool, U, ValidTypes...> operator()(U&&) { |
| return false; |
| } |
| template <typename U> |
| detail::EnableIfElement<bool, U, ValidTypes...> operator()(const U& value) { |
| std::forward<Op>(op)(value); |
| return true; |
| } |
| template <typename U> |
| detail::EnableIfElement<bool, U, ValidTypes...> operator()(U&& value) { |
| std::forward<Op>(op)(std::forward<U>(value)); |
| return true; |
| } |
| }; |
| }; |
| |
| } // namespace rpc |
| } // namespace pdx |
| } // namespace android |
| |
| // Overloads of std::get<T> and std::get<I> for android::pdx::rpc::Variant. |
| namespace std { |
| |
| template <typename T, typename... Types> |
| inline T& get(::android::pdx::rpc::Variant<Types...>& v) { |
| return *v.template get<T>(); |
| } |
| template <typename T, typename... Types> |
| inline T&& get(::android::pdx::rpc::Variant<Types...>&& v) { |
| return std::move(*v.template get<T>()); |
| } |
| template <typename T, typename... Types> |
| inline const T& get(const ::android::pdx::rpc::Variant<Types...>& v) { |
| return *v.template get<T>(); |
| } |
| template <std::size_t I, typename... Types> |
| inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get( |
| ::android::pdx::rpc::Variant<Types...>& v) { |
| return *v.template get<I>(); |
| } |
| template <std::size_t I, typename... Types> |
| inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>&& get( |
| ::android::pdx::rpc::Variant<Types...>&& v) { |
| return std::move(*v.template get<I>()); |
| } |
| template <std::size_t I, typename... Types> |
| inline const ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get( |
| const ::android::pdx::rpc::Variant<Types...>& v) { |
| return *v.template get<I>(); |
| } |
| |
| } // namespace std |
| |
| #endif // ANDROID_PDX_RPC_VARIANT_H_ |