blob: 2cc96645e249479a2731b2f464e0a8e2ec2cc848 [file] [log] [blame]
#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_} {}
// 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 <typename... OtherTypes>
explicit Variant(const Variant<OtherTypes...>& other) {
other.Visit([this](const auto& value) { Construct(value); });
}
#pragma clang diagnostic pop
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_