mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-13 19:01:24 +00:00
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:
parent
e1699caf81
commit
b68e8aa658
@ -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
|
||||
|
153
src/castable.h
153
src/castable.h
@ -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();
|
||||
|
52
src/traits.h
52
src/traits.h
@ -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_
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user