#ifndef ANDROID_PDX_RPC_VARIANT_H_ #define ANDROID_PDX_RPC_VARIANT_H_ #include #include #include namespace android { namespace pdx { namespace rpc { // Type tag denoting an empty variant. struct EmptyVariant {}; namespace detail { // Type for matching tagged overloads. template struct TypeTag {}; // Determines the type of the I-th element of Types.... template using TypeForIndex = std::tuple_element_t>; // Determines the type tag for the I-th element of Types.... template using TypeTagForIndex = TypeTag>; // 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 v; // const int array[3] = {1, 2, 3}; // v = array; // This is allowed by regular std::is_constructible. // template struct IsConstructible; template struct IsConstructible : std::integral_constant::value && !(std::is_same, bool>::value && std::is_pointer>::value)> {}; template struct IsConstructible : std::is_constructible {}; // Enable if T(Args...) is well formed. template using EnableIfConstructible = typename std::enable_if::value, R>::type; // Enable if T(Args...) is not well formed. template using EnableIfNotConstructible = typename std::enable_if::value, R>::type; // Determines whether T is an element of Types...; template struct HasType : std::false_type {}; template struct HasType : std::is_same, std::decay_t> {}; template struct HasType : std::integral_constant::value || HasType::value> {}; // Defines set operations on a set of Types... template struct Set { // Default specialization catches the empty set, which is always a subset. template struct IsSubset : std::true_type {}; template struct IsSubset : HasType {}; template struct IsSubset : std::integral_constant::value && IsSubset::value> {}; }; // Determines the number of elements of Types... that are constructible from // From. template struct ConstructibleCount; template struct ConstructibleCount : std::integral_constant::value> {}; template struct ConstructibleCount : std::integral_constant::value + ConstructibleCount::value> {}; // Enable if T is an element of Types... template using EnableIfElement = typename std::enable_if::value, R>::type; // Enable if T is not an element of Types... template using EnableIfNotElement = typename std::enable_if::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 using EnableIfConvertible = typename std::enable_if::value && ConstructibleCount::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 using EnableIfAssignable = typename std::enable_if::value || ConstructibleCount::value == 1, R>::type; // Selects a type for SFINAE constructor selection. template using Select = std::conditional_t; // Recursive union type. template union Union; // Specialization handling a singular type, terminating template recursion. template union Union { Union() {} ~Union() {} template Union(std::int32_t index, std::int32_t* index_out, TypeTag, T&& value) : first_(std::forward(value)) { *index_out = index; } template > Union(std::int32_t index, std::int32_t* index_out, T&& value) : first_(std::forward(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) { return first_; } const Type& get(TypeTag) const { return first_; } EmptyVariant get(TypeTag) const { return {}; } constexpr std::int32_t index(TypeTag) const { return 0; } template std::int32_t Construct(TypeTag, Args&&... args) { new (&first_) Type(std::forward(args)...); return 0; } template EnableIfConstructible Construct(Args&&... args) { new (&first_) Type(std::forward(args)...); return 0; } void Destruct(std::int32_t target_index) { if (target_index == index(TypeTag{})) { (&get(TypeTag{}))->~Type(); } } template bool Assign(TypeTag, std::int32_t target_index, T&& value) { if (target_index == 0) { first_ = std::forward(value); return true; } else { return false; } } template EnableIfConstructible Assign(std::int32_t target_index, T&& value) { if (target_index == 0) { first_ = std::forward(value); return true; } else { return false; } } template EnableIfNotConstructible Assign(std::int32_t /*target_index*/, T&& /*value*/) { return false; } template decltype(auto) Visit(std::int32_t target_index, Op&& op) { if (target_index == index(TypeTag{})) return std::forward(op)(get(TypeTag{})); else return std::forward(op)(get(TypeTag{})); } template decltype(auto) Visit(std::int32_t target_index, Op&& op) const { if (target_index == index(TypeTag{})) return std::forward(op)(get(TypeTag{})); else return std::forward(op)(get(TypeTag{})); } template bool Become(std::int32_t target_index, Args&&... args) { if (target_index == index(TypeTag{})) { Construct(TypeTag{}, std::forward(args)...); return true; } else { return false; } } private: Type first_; }; // Specialization that recursively unions types from the paramater pack. template union Union { Union() {} ~Union() {} template Union(std::int32_t index, std::int32_t* index_out, TypeTag, T&& value) : first_(std::forward(value)) { *index_out = index; } template Union(std::int32_t index, std::int32_t* index_out, TypeTag, U&& value) : rest_(index + 1, index_out, TypeTag{}, std::forward(value)) {} Union(const Union& other, std::int32_t index) { if (index == 0) new (&first_) First(other.first_); else new (&rest_) Union(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(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 using SelectConstructor = Select::value == 1, FirstType, RestType>; template Union(std::int32_t index, std::int32_t* index_out, T&& value) : Union(index, index_out, std::forward(value), SelectConstructor{}) {} template Union(std::int32_t index, std::int32_t* index_out, T&& value, FirstType) : first_(std::forward(value)) { *index_out = index; } template Union(std::int32_t index, std::int32_t* index_out, T&& value, RestType) : rest_(index + 1, index_out, std::forward(value)) {} First& get(TypeTag) { return first_; } const First& get(TypeTag) const { return first_; } constexpr std::int32_t index(TypeTag) const { return 0; } template T& get(TypeTag) { return rest_.get(TypeTag{}); } template const T& get(TypeTag) const { return rest_.template get(TypeTag{}); } template constexpr std::int32_t index(TypeTag) const { return 1 + rest_.index(TypeTag{}); } template std::int32_t Construct(TypeTag, Args&&... args) { new (&first_) First(std::forward(args)...); return 0; } template std::int32_t Construct(TypeTag, Args&&... args) { return 1 + rest_.template Construct(TypeTag{}, std::forward(args)...); } template EnableIfConstructible Construct( Args&&... args) { new (&first_) First(std::forward(args)...); return 0; } template EnableIfNotConstructible Construct( Args&&... args) { return 1 + rest_.template Construct(std::forward(args)...); } void Destruct(std::int32_t target_index) { if (target_index == index(TypeTag{})) { (get(TypeTag{})).~First(); } else { rest_.Destruct(target_index - 1); } } template bool Assign(TypeTag, std::int32_t target_index, T&& value) { if (target_index == 0) { first_ = std::forward(value); return true; } else { return false; } } template bool Assign(TypeTag, std::int32_t target_index, U&& value) { return rest_.Assign(TypeTag{}, target_index - 1, std::forward(value)); } template EnableIfConstructible Assign(std::int32_t target_index, T&& value) { if (target_index == 0) { first_ = std::forward(value); return true; } else { return rest_.Assign(target_index - 1, std::forward(value)); } } template EnableIfNotConstructible Assign(std::int32_t target_index, T&& value) { return rest_.Assign(target_index - 1, std::forward(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 decltype(auto) Visit(std::int32_t target_index, Op&& op) { if (target_index == index(TypeTag{})) return std::forward(op)(get(TypeTag{})); else return rest_.Visit(target_index - 1, std::forward(op)); } template decltype(auto) Visit(std::int32_t target_index, Op&& op) const { if (target_index == index(TypeTag{})) return std::forward(op)(get(TypeTag{})); else return rest_.Visit(target_index - 1, std::forward(op)); } template bool Become(std::int32_t target_index, Args&&... args) { if (target_index == index(TypeTag{})) { Construct(TypeTag{}, std::forward(args)...); return true; } else { return rest_.Become(target_index - 1, std::forward(args)...); } } private: First first_; Union 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 class Variant { private: // Convenience types. template using TypeTag = detail::TypeTag; template using DecayedTypeTag = TypeTag>; template using TypeForIndex = detail::TypeForIndex; template using TypeTagForIndex = detail::TypeTagForIndex; template using HasType = detail::HasType; template using EnableIfElement = detail::EnableIfElement; template using EnableIfConvertible = detail::EnableIfConvertible; template using EnableIfAssignable = detail::EnableIfAssignable; struct Direct {}; struct Convert {}; template using SelectConstructor = detail::Select::value, Direct, Convert>; // Constructs by type tag when T is an direct element of Types... template explicit Variant(T&& value, Direct) : value_(0, &index_, DecayedTypeTag{}, std::forward(value)) {} // Conversion constructor when T is not a direct element of Types... template explicit Variant(T&& value, Convert) : value_(0, &index_, std::forward(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) noexcept : index_{other.index_}, value_{std::move(other.value_), other.index_} {} // Recent Clang versions has a regression that produces bogus // unused-lambda-capture warning. Suppress the warning as a temporary // workaround. http://b/71356631 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-lambda-capture" // Copy and move construction from Variant types. Each element of OtherTypes // must be convertible to an element of Types. template explicit Variant(const Variant& other) { other.Visit([this](const auto& value) { Construct(value); }); } #pragma clang diagnostic pop template explicit Variant(Variant&& 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) noexcept { other.Visit([this](auto&& value) { *this = std::move(value); }); return *this; } // Construction from non-Variant types. template > explicit Variant(T&& value) : Variant(std::forward(value), SelectConstructor{}) {} // 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 EnableIfElement operator=(T&& value) { Assign(DecayedTypeTag{}, std::forward(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 EnableIfConvertible operator=(T&& value) { Assign(std::forward(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 Variant& operator=(const Variant& other) { other.Visit([this](const auto& value) { *this = value; }); return *this; } template Variant& operator=(Variant&& 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 void Become(std::int32_t target_index, Args&&... args) { if (target_index != index()) { Destruct(); index_ = value_.Become(target_index, std::forward(args)...) ? target_index : kEmptyIndex; } } // Invokes |Op| on the active element. If the Variant is empty |Op| is invoked // on EmptyVariant. template decltype(auto) Visit(Op&& op) { return value_.Visit(index_, std::forward(op)); } template decltype(auto) Visit(Op&& op) const { return value_.Visit(index_, std::forward(op)); } // Index returned when the Variant is empty. enum : std::int32_t { kEmptyIndex = -1 }; // Returns the index of the given type. template constexpr std::int32_t index_of() const { static_assert(HasType::value, "T is not an element type of Variant."); return value_.index(DecayedTypeTag{}); } // 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 bool is() const { static_assert(HasType::value, "T is not an element type of Variant."); return index() == index_of(); } // 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 T* get() { if (is()) return &value_.get(DecayedTypeTag{}); else return nullptr; } template const T* get() const { if (is()) return &value_.template get(DecayedTypeTag{}); else return nullptr; } template TypeForIndex* get() { if (is>()) return &value_.get(TypeTagForIndex{}); else return nullptr; } template const TypeForIndex* get() const { if (is>()) return &value_.template get(TypeTagForIndex{}); else return nullptr; } private: std::int32_t index_ = kEmptyIndex; detail::Union...> value_; // Constructs an element from the given arguments and sets the Variant to the // resulting type. template void Construct(Args&&... args) { index_ = value_.template Construct(std::forward(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 void Assign(TypeTag, U&& value) { if (!value_.template Assign(TypeTag{}, index_, std::forward(value))) { Destruct(); Construct(TypeTag{}, std::forward(value)); } } template void Assign(T&& value) { if (!value_.template Assign(index_, std::forward(value))) { Destruct(); Construct(std::forward(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 v(10); // bool bool_value; // if (IfAnyOf::Get(v, &bool_value)) { // DoSomething(bool_value); // } else { // HandleInvalidType(); // } // IfAnyOf::Call(v, [](const auto& value) { DoSomething(value); }); // template 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 static bool Call(Variant* variant, Op&& op) { static_assert( detail::Set::template IsSubset::value, "ValidTypes may only contain element types from the Variant."); return variant->Visit(CallOp{std::forward(op)}); } template static bool Call(const Variant* variant, Op&& op) { static_assert( detail::Set::template IsSubset::value, "ValidTypes may only contain element types from the Variant."); return variant->Visit(CallOp{std::forward(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 static bool Get(const Variant* 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 static bool Take(Variant* 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 static bool Swap(Variant* variant, T* value_out) { return Call(variant, [value_out](auto&& value) { std::swap(*value_out, value); }); } private: template struct CallOp { Op&& op; template detail::EnableIfNotElement operator()(U&&) { return false; } template detail::EnableIfElement operator()(const U& value) { std::forward(op)(value); return true; } template detail::EnableIfElement operator()(U&& value) { std::forward(op)(std::forward(value)); return true; } }; }; } // namespace rpc } // namespace pdx } // namespace android // Overloads of std::get and std::get for android::pdx::rpc::Variant. namespace std { template inline T& get(::android::pdx::rpc::Variant& v) { return *v.template get(); } template inline T&& get(::android::pdx::rpc::Variant&& v) { return std::move(*v.template get()); } template inline const T& get(const ::android::pdx::rpc::Variant& v) { return *v.template get(); } template inline ::android::pdx::rpc::detail::TypeForIndex& get( ::android::pdx::rpc::Variant& v) { return *v.template get(); } template inline ::android::pdx::rpc::detail::TypeForIndex&& get( ::android::pdx::rpc::Variant&& v) { return std::move(*v.template get()); } template inline const ::android::pdx::rpc::detail::TypeForIndex& get( const ::android::pdx::rpc::Variant& v) { return *v.template get(); } } // namespace std #endif // ANDROID_PDX_RPC_VARIANT_H_