Optimize tint::IsAnyOf<>() for many types

Split IsAnyOf() into log(n) stages, where each stage performs a hashcode
check.

Previously there was a single hash test across the bitwise-or of all the
types being considered. If this passed, then each type would be tested
with Is<T>() individually. With this change, the list of types will be
recursively split into two, which each block hash-code checked. This is
repeated until we reach fewer than 4 types to check, where the test
decays to using Is<T>() for each type.

Also renamed `combined_hashcode` to `full_hashcode`, and used the term
CombinedHash for new helpers that bitwise-or the hashes from a number
of types.

Bug: tint:1383
Change-Id: Id056b9f7a9792430bd75ce554cb5fe73221ca4c7
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/78580
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2022-02-02 14:38:32 +00:00 committed by Tint LUCI CQ
parent e1699caf81
commit b68e8aa658
4 changed files with 273 additions and 115 deletions

View File

@ -23,7 +23,7 @@ const TypeInfo detail::TypeInfoOf<CastableBase>::info{
nullptr,
"CastableBase",
tint::TypeInfo::HashCodeOf<CastableBase>(),
tint::TypeInfo::HashCodeOf<CastableBase>(),
tint::TypeInfo::FullHashCodeOf<CastableBase>(),
};
} // namespace tint

View File

@ -17,6 +17,7 @@
#include <stdint.h>
#include <functional>
#include <tuple>
#include <utility>
#include "src/traits.h"
@ -61,7 +62,7 @@ struct TypeInfoOf;
&tint::detail::TypeInfoOf<CLASS::TrueBase>::info, \
#CLASS, \
tint::TypeInfo::HashCodeOf<CLASS>(), \
tint::TypeInfo::CombinedHashCodeOf<CLASS>(), \
tint::TypeInfo::FullHashCodeOf<CLASS>(), \
}; \
TINT_CASTABLE_POP_DISABLE_WARNINGS()
@ -86,17 +87,17 @@ struct TypeInfo {
const char* name;
/// The type hash code
const HashCode hashcode;
/// The type hash code or'd with the base class' combined hash code
const HashCode combined_hashcode;
/// The type hash code bitwise-or'd with all ancestor's hashcodes.
const HashCode full_hashcode;
/// @param type the test type info
/// @returns true if the class with this TypeInfo is of, or derives from the
/// class with the given TypeInfo.
inline bool Is(const tint::TypeInfo* type) const {
// Optimization: Check whether the all the bits of the type's hashcode can
// be found in the combined_hashcode. If a single bit is missing, then we
// be found in the full_hashcode. If a single bit is missing, then we
// can quickly tell that that this TypeInfo does not derive from `type`.
if ((combined_hashcode & type->hashcode) != type->hashcode) {
if ((full_hashcode & type->hashcode) != type->hashcode) {
return false;
}
@ -145,6 +146,8 @@ struct TypeInfo {
/// multiple hashcodes are bitwise-or'd together.
template <typename T>
static constexpr HashCode HashCodeOf() {
static_assert(traits::IsTypeOrDerived<T, CastableBase>::value,
"T is not Castable");
/// Use the compiler's "pretty" function name, which includes the template
/// type, to obtain a unique hash value.
#ifdef _MSC_VER
@ -161,13 +164,75 @@ struct TypeInfo {
/// @returns the hashcode of the given type, bitwise-or'd with the hashcodes
/// of all base classes.
template <typename T>
static constexpr HashCode CombinedHashCodeOf() {
static constexpr HashCode FullHashCodeOf() {
if constexpr (std::is_same_v<T, CastableBase>) {
return HashCodeOf<CastableBase>();
} else {
return HashCodeOf<T>() | CombinedHashCodeOf<typename T::TrueBase>();
return HashCodeOf<T>() | FullHashCodeOf<typename T::TrueBase>();
}
}
/// @returns the bitwise-or'd hashcodes of all the types of the tuple `TUPLE`.
/// @see HashCodeOf
template <typename TUPLE>
static constexpr HashCode CombinedHashCodeOfTuple() {
constexpr auto kCount = std::tuple_size_v<TUPLE>;
if constexpr (kCount == 0) {
return 0;
} else if constexpr (kCount == 1) {
return HashCodeOf<std::tuple_element_t<0, TUPLE>>();
} else {
constexpr auto kMid = kCount / 2;
return CombinedHashCodeOfTuple<traits::SliceTuple<0, kMid, TUPLE>>() |
CombinedHashCodeOfTuple<
traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
}
}
/// @returns the bitwise-or'd hashcodes of all the template parameter types.
/// @see HashCodeOf
template <typename... TYPES>
static constexpr HashCode CombinedHashCodeOf() {
return CombinedHashCodeOfTuple<std::tuple<TYPES...>>();
}
/// @returns true if this TypeInfo is of, or derives from any of the types in
/// `TUPLE`.
template <typename TUPLE>
inline bool IsAnyOfTuple() const {
constexpr auto kCount = std::tuple_size_v<TUPLE>;
if constexpr (kCount == 0) {
return false;
} else if constexpr (kCount == 1) {
return Is(&Of<std::tuple_element_t<0, TUPLE>>());
} else if constexpr (kCount == 2) {
return Is(&Of<std::tuple_element_t<0, TUPLE>>()) ||
Is(&Of<std::tuple_element_t<1, TUPLE>>());
} else if constexpr (kCount == 3) {
return Is(&Of<std::tuple_element_t<0, TUPLE>>()) ||
Is(&Of<std::tuple_element_t<1, TUPLE>>()) ||
Is(&Of<std::tuple_element_t<2, TUPLE>>());
} else {
// Optimization: Compare the object's hashcode to the bitwise-or of all
// the tested type's hashcodes. If there's no intersection of bits in
// the two masks, then we can guarantee that the type is not in `TO`.
if (full_hashcode & TypeInfo::CombinedHashCodeOfTuple<TUPLE>()) {
// Possibly one of the types in `TUPLE`.
// Split the search in two, and scan each block.
static constexpr auto kMid = kCount / 2;
return IsAnyOfTuple<traits::SliceTuple<0, kMid, TUPLE>>() ||
IsAnyOfTuple<traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
}
return false;
}
}
/// @returns true if this TypeInfo is of, or derives from any of the types in
/// `TYPES`.
template <typename... TYPES>
inline bool IsAnyOf() const {
return IsAnyOfTuple<std::tuple<TYPES...>>();
}
};
namespace detail {
@ -181,10 +246,6 @@ struct TypeInfoOf {
static const TypeInfo info;
};
// Forward declaration
template <typename TO_FIRST, typename... TO_REST>
struct IsAnyOf;
/// A placeholder structure used for template parameters that need a default
/// type, but can always be automatically inferred.
struct Infer;
@ -204,39 +265,29 @@ inline bool Is(FROM* obj) {
}
/// @returns true if `obj` is a valid pointer, and is of, or derives from the
/// class `TO`, and pred(const TO*) returns true
/// type `TYPE`, and pred(const TYPE*) returns true
/// @param obj the object to test from
/// @param pred predicate function with signature `bool(const TO*)` called iff
/// object is of, or derives from the class `TO`.
/// @param pred predicate function with signature `bool(const TYPE*)` called iff
/// object is of, or derives from the class `TYPE`.
/// @see CastFlags
template <typename TO,
template <typename TYPE,
int FLAGS = 0,
typename FROM = detail::Infer,
typename OBJ = detail::Infer,
typename Pred = detail::Infer>
inline bool Is(FROM* obj, Pred&& pred) {
return Is<TO, FLAGS, FROM>(obj) &&
pred(static_cast<std::add_const_t<TO>*>(obj));
inline bool Is(OBJ* obj, Pred&& pred) {
return Is<TYPE, FLAGS, OBJ>(obj) &&
pred(static_cast<std::add_const_t<TYPE>*>(obj));
}
/// @returns true if `obj` is of, or derives from any of the `TO`
/// classes.
/// @param obj the object to cast from
template <typename... TO, typename FROM>
inline bool IsAnyOf(FROM* obj) {
/// @returns true if `obj` is a valid pointer, and is of, or derives from any of
/// the types in `TYPES`.OBJ
/// @param obj the object to query.
template <typename... TYPES, typename OBJ>
inline bool IsAnyOf(OBJ* obj) {
if (!obj) {
return false;
}
// Optimization: Compare the object's combined_hashcode to the bitwise-or of
// all the tested type's hashcodes. If there's no intersection of bits in the
// two masks, then we can guarantee that the type is not in `TO`.
using Helper = detail::IsAnyOf<TO...>;
auto* type = &obj->TypeInfo();
auto hashcode = type->combined_hashcode;
if ((Helper::kHashCodes & hashcode) == 0) {
return false;
}
// Possibly one of the types in `TO`. Continue to testing against each type.
return Helper::template Exec<FROM>(type);
return obj->TypeInfo().template IsAnyOf<TYPES...>();
}
/// @returns obj dynamically cast to the type `TO` or `nullptr` if
@ -402,38 +453,6 @@ class Castable : public BASE {
}
};
namespace detail {
/// Helper for Castable::IsAnyOf
template <typename TO_FIRST, typename... TO_REST>
struct IsAnyOf {
/// The bitwise-or of all typeinfo hashcodes
static constexpr auto kHashCodes =
TypeInfo::HashCodeOf<TO_FIRST>() | IsAnyOf<TO_REST...>::kHashCodes;
/// @param type castable object type to test
/// @returns true if `obj` is of, or derives from any of `[TO_FIRST,
/// ...TO_REST]`
template <typename FROM>
static bool Exec(const TypeInfo* type) {
return TypeInfo::Is<TO_FIRST, FROM>(type) ||
IsAnyOf<TO_REST...>::template Exec<FROM>(type);
}
};
/// Terminal specialization
template <typename TO>
struct IsAnyOf<TO> {
/// The bitwise-or of all typeinfo hashcodes
static constexpr auto kHashCodes = TypeInfo::HashCodeOf<TO>();
/// @param type castable object type to test
/// @returns true if `obj` is of, or derives from TO
template <typename FROM>
static bool Exec(const TypeInfo* type) {
return TypeInfo::Is<TO, FROM>(type);
}
};
} // namespace detail
} // namespace tint
TINT_CASTABLE_POP_DISABLE_WARNINGS();

View File

@ -16,9 +16,9 @@
#define SRC_TRAITS_H_
#include <tuple>
#include <utility>
namespace tint {
namespace traits {
namespace tint::traits {
/// Convience type definition for std::decay<T>::type
template <typename T>
@ -109,7 +109,51 @@ using EnableIfIsType = EnableIf<IsTypeOrDerived<T, BASE>::value, T>;
template <typename T, typename BASE>
using EnableIfIsNotType = EnableIf<!IsTypeOrDerived<T, BASE>::value, T>;
} // namespace traits
} // namespace tint
/// @returns the std::index_sequence with all the indices shifted by OFFSET.
template <std::size_t OFFSET, std::size_t... INDICES>
constexpr auto Shift(std::index_sequence<INDICES...>) {
return std::integer_sequence<std::size_t, OFFSET + INDICES...>{};
}
/// @returns a std::integer_sequence with the integers `[OFFSET..OFFSET+COUNT)`
template <std::size_t OFFSET, std::size_t COUNT>
constexpr auto Range() {
return Shift<OFFSET>(std::make_index_sequence<COUNT>{});
}
namespace detail {
/// @returns the tuple `t` swizzled by `INDICES`
template <class TUPLE, std::size_t... INDICES>
constexpr auto Swizzle(TUPLE&& t, std::index_sequence<INDICES...>) {
return std::make_tuple(std::get<INDICES>(std::forward<TUPLE>(t))...);
}
/// @returns a nullptr of the tuple type `TUPLE` swizzled by `INDICES`.
/// @note: This function is intended to be used in a `decltype()` expression,
/// and returns a pointer-to-tuple as the tuple may hold non-constructable
/// types.
template <class TUPLE, std::size_t... INDICES>
constexpr auto* SwizzlePtrTy(std::index_sequence<INDICES...>) {
using Swizzled = std::tuple<std::tuple_element_t<INDICES, TUPLE>...>;
return static_cast<Swizzled*>(nullptr);
}
} // namespace detail
/// @returns the slice of the tuple `t` with the tuple elements
/// `[OFFSET..OFFSET+COUNT)`
template <std::size_t OFFSET, std::size_t COUNT, typename TUPLE>
constexpr auto Slice(TUPLE&& t) {
return detail::Swizzle<TUPLE>(std::forward<TUPLE>(t), Range<OFFSET, COUNT>());
}
/// Resolves to the slice of the tuple `t` with the tuple elements
/// `[OFFSET..OFFSET+COUNT)`
template <std::size_t OFFSET, std::size_t COUNT, typename TUPLE>
using SliceTuple = std::remove_pointer_t<decltype(
detail::SwizzlePtrTy<TUPLE>(Range<OFFSET, COUNT>()))>;
} // namespace tint::traits
#endif // SRC_TRAITS_H_

View File

@ -28,13 +28,12 @@ void F3(int, S, float) {}
TEST(ParamType, Function) {
F1({}); // Avoid unused method warning
F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&F3), 0>, int>::value, "");
static_assert(std::is_same<ParameterType<decltype(&F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&F3), 2>, float>::value,
"");
static_assert(std::is_same<ReturnType<decltype(&F1)>, void>::value, "");
static_assert(std::is_same<ReturnType<decltype(&F3)>, void>::value, "");
static_assert(std::is_same_v<ParameterType<decltype(&F1), 0>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&F3), 0>, int>, "");
static_assert(std::is_same_v<ParameterType<decltype(&F3), 1>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&F3), 2>, float>, "");
static_assert(std::is_same_v<ReturnType<decltype(&F1)>, void>, "");
static_assert(std::is_same_v<ReturnType<decltype(&F3)>, void>, "");
static_assert(SignatureOfT<decltype(&F1)>::parameter_count == 1, "");
static_assert(SignatureOfT<decltype(&F3)>::parameter_count == 3, "");
}
@ -47,14 +46,12 @@ TEST(ParamType, Method) {
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
"");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
"");
static_assert(std::is_same<ReturnType<decltype(&C::F1)>, void>::value, "");
static_assert(std::is_same<ReturnType<decltype(&C::F3)>, void>::value, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F1), 0>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 0>, int>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 1>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 2>, float>, "");
static_assert(std::is_same_v<ReturnType<decltype(&C::F1)>, void>, "");
static_assert(std::is_same_v<ReturnType<decltype(&C::F3)>, void>, "");
static_assert(SignatureOfT<decltype(&C::F1)>::parameter_count == 1, "");
static_assert(SignatureOfT<decltype(&C::F3)>::parameter_count == 3, "");
}
@ -67,14 +64,12 @@ TEST(ParamType, ConstMethod) {
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
"");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
"");
static_assert(std::is_same<ReturnType<decltype(&C::F1)>, void>::value, "");
static_assert(std::is_same<ReturnType<decltype(&C::F3)>, void>::value, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F1), 0>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 0>, int>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 1>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 2>, float>, "");
static_assert(std::is_same_v<ReturnType<decltype(&C::F1)>, void>, "");
static_assert(std::is_same_v<ReturnType<decltype(&C::F3)>, void>, "");
static_assert(SignatureOfT<decltype(&C::F1)>::parameter_count == 1, "");
static_assert(SignatureOfT<decltype(&C::F3)>::parameter_count == 3, "");
}
@ -87,14 +82,12 @@ TEST(ParamType, StaticMethod) {
};
C::F1({}); // Avoid unused method warning
C::F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
"");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
"");
static_assert(std::is_same<ReturnType<decltype(&C::F1)>, void>::value, "");
static_assert(std::is_same<ReturnType<decltype(&C::F3)>, void>::value, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F1), 0>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 0>, int>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 1>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(&C::F3), 2>, float>, "");
static_assert(std::is_same_v<ReturnType<decltype(&C::F1)>, void>, "");
static_assert(std::is_same_v<ReturnType<decltype(&C::F3)>, void>, "");
static_assert(SignatureOfT<decltype(&C::F1)>::parameter_count == 1, "");
static_assert(SignatureOfT<decltype(&C::F3)>::parameter_count == 3, "");
}
@ -102,12 +95,12 @@ TEST(ParamType, StaticMethod) {
TEST(ParamType, FunctionLike) {
using F1 = std::function<void(S)>;
using F3 = std::function<void(int, S, float)>;
static_assert(std::is_same<ParameterType<F1, 0>, S>::value, "");
static_assert(std::is_same<ParameterType<F3, 0>, int>::value, "");
static_assert(std::is_same<ParameterType<F3, 1>, S>::value, "");
static_assert(std::is_same<ParameterType<F3, 2>, float>::value, "");
static_assert(std::is_same<ReturnType<F1>, void>::value, "");
static_assert(std::is_same<ReturnType<F3>, void>::value, "");
static_assert(std::is_same_v<ParameterType<F1, 0>, S>, "");
static_assert(std::is_same_v<ParameterType<F3, 0>, int>, "");
static_assert(std::is_same_v<ParameterType<F3, 1>, S>, "");
static_assert(std::is_same_v<ParameterType<F3, 2>, float>, "");
static_assert(std::is_same_v<ReturnType<F1>, void>, "");
static_assert(std::is_same_v<ReturnType<F3>, void>, "");
static_assert(SignatureOfT<F1>::parameter_count == 1, "");
static_assert(SignatureOfT<F3>::parameter_count == 3, "");
}
@ -115,15 +108,117 @@ TEST(ParamType, FunctionLike) {
TEST(ParamType, Lambda) {
auto l1 = [](S) {};
auto l3 = [](int, S, float) {};
static_assert(std::is_same<ParameterType<decltype(l1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(l3), 0>, int>::value, "");
static_assert(std::is_same<ParameterType<decltype(l3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(l3), 2>, float>::value, "");
static_assert(std::is_same<ReturnType<decltype(l1)>, void>::value, "");
static_assert(std::is_same<ReturnType<decltype(l3)>, void>::value, "");
static_assert(std::is_same_v<ParameterType<decltype(l1), 0>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(l3), 0>, int>, "");
static_assert(std::is_same_v<ParameterType<decltype(l3), 1>, S>, "");
static_assert(std::is_same_v<ParameterType<decltype(l3), 2>, float>, "");
static_assert(std::is_same_v<ReturnType<decltype(l1)>, void>, "");
static_assert(std::is_same_v<ReturnType<decltype(l3)>, void>, "");
static_assert(SignatureOfT<decltype(l1)>::parameter_count == 1, "");
static_assert(SignatureOfT<decltype(l3)>::parameter_count == 3, "");
}
TEST(Slice, Empty) {
auto sliced = Slice<0, 0>(std::make_tuple<>());
static_assert(std::tuple_size_v<decltype(sliced)> == 0, "");
}
TEST(Slice, SingleElementSliceEmpty) {
auto sliced = Slice<0, 0>(std::make_tuple<int>(1));
static_assert(std::tuple_size_v<decltype(sliced)> == 0, "");
}
TEST(Slice, SingleElementSliceFull) {
auto sliced = Slice<0, 1>(std::make_tuple<int>(1));
static_assert(std::tuple_size_v<decltype(sliced)> == 1, "");
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(sliced)>, int>,
"");
EXPECT_EQ(std::get<0>(sliced), 1);
}
TEST(Slice, MixedTupleSliceEmpty) {
auto sliced = Slice<1, 0>(std::make_tuple<int, bool, float>(1, true, 2.0f));
static_assert(std::tuple_size_v<decltype(sliced)> == 0, "");
}
TEST(Slice, MixedTupleSliceFull) {
auto sliced = Slice<0, 3>(std::make_tuple<int, bool, float>(1, true, 2.0f));
static_assert(std::tuple_size_v<decltype(sliced)> == 3, "");
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(sliced)>, int>,
"");
static_assert(std::is_same_v<std::tuple_element_t<1, decltype(sliced)>, bool>,
"");
static_assert(
std::is_same_v<std::tuple_element_t<2, decltype(sliced)>, float>, "");
EXPECT_EQ(std::get<0>(sliced), 1);
EXPECT_EQ(std::get<1>(sliced), true);
EXPECT_EQ(std::get<2>(sliced), 2.0f);
}
TEST(Slice, MixedTupleSliceLowPart) {
auto sliced = Slice<0, 2>(std::make_tuple<int, bool, float>(1, true, 2.0f));
static_assert(std::tuple_size_v<decltype(sliced)> == 2, "");
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(sliced)>, int>,
"");
static_assert(std::is_same_v<std::tuple_element_t<1, decltype(sliced)>, bool>,
"");
EXPECT_EQ(std::get<0>(sliced), 1);
EXPECT_EQ(std::get<1>(sliced), true);
}
TEST(Slice, MixedTupleSliceHighPart) {
auto sliced = Slice<1, 2>(std::make_tuple<int, bool, float>(1, true, 2.0f));
static_assert(std::tuple_size_v<decltype(sliced)> == 2, "");
static_assert(std::is_same_v<std::tuple_element_t<0, decltype(sliced)>, bool>,
"");
static_assert(
std::is_same_v<std::tuple_element_t<1, decltype(sliced)>, float>, "");
EXPECT_EQ(std::get<0>(sliced), true);
EXPECT_EQ(std::get<1>(sliced), 2.0f);
}
TEST(SliceTuple, Empty) {
using sliced = SliceTuple<0, 0, std::tuple<>>;
static_assert(std::tuple_size_v<sliced> == 0, "");
}
TEST(SliceTuple, SingleElementSliceEmpty) {
using sliced = SliceTuple<0, 0, std::tuple<int>>;
static_assert(std::tuple_size_v<sliced> == 0, "");
}
TEST(SliceTuple, SingleElementSliceFull) {
using sliced = SliceTuple<0, 1, std::tuple<int>>;
static_assert(std::tuple_size_v<sliced> == 1, "");
static_assert(std::is_same_v<std::tuple_element_t<0, sliced>, int>, "");
}
TEST(SliceTuple, MixedTupleSliceEmpty) {
using sliced = SliceTuple<1, 0, std::tuple<int, bool, float>>;
static_assert(std::tuple_size_v<sliced> == 0, "");
}
TEST(SliceTuple, MixedTupleSliceFull) {
using sliced = SliceTuple<0, 3, std::tuple<int, bool, float>>;
static_assert(std::tuple_size_v<sliced> == 3, "");
static_assert(std::is_same_v<std::tuple_element_t<0, sliced>, int>, "");
static_assert(std::is_same_v<std::tuple_element_t<1, sliced>, bool>, "");
static_assert(std::is_same_v<std::tuple_element_t<2, sliced>, float>, "");
}
TEST(SliceTuple, MixedTupleSliceLowPart) {
using sliced = SliceTuple<0, 2, std::tuple<int, bool, float>>;
static_assert(std::tuple_size_v<sliced> == 2, "");
static_assert(std::is_same_v<std::tuple_element_t<0, sliced>, int>, "");
static_assert(std::is_same_v<std::tuple_element_t<1, sliced>, bool>, "");
}
TEST(SliceTuple, MixedTupleSliceHighPart) {
using sliced = SliceTuple<1, 2, std::tuple<int, bool, float>>;
static_assert(std::tuple_size_v<sliced> == 2, "");
static_assert(std::is_same_v<std::tuple_element_t<0, sliced>, bool>, "");
static_assert(std::is_same_v<std::tuple_element_t<1, sliced>, float>, "");
}
} // namespace traits
} // namespace tint