| // Copyright 2020 The Tint Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef SRC_CASTABLE_H_ |
| #define SRC_CASTABLE_H_ |
| |
| #include <stdint.h> |
| #include <functional> |
| #include <utility> |
| |
| #include "src/traits.h" |
| #include "src/utils/crc32.h" |
| |
| #if defined(__clang__) |
| /// Temporarily disable certain warnings when using Castable API |
| #define TINT_CASTABLE_PUSH_DISABLE_WARNINGS() \ |
| _Pragma("clang diagnostic push") /**/ \ |
| _Pragma("clang diagnostic ignored \"-Wundefined-var-template\"") /**/ \ |
| static_assert(true, "require extra semicolon") |
| |
| /// Restore disabled warnings |
| #define TINT_CASTABLE_POP_DISABLE_WARNINGS() \ |
| _Pragma("clang diagnostic pop") /**/ \ |
| static_assert(true, "require extra semicolon") |
| #else |
| #define TINT_CASTABLE_PUSH_DISABLE_WARNINGS() \ |
| static_assert(true, "require extra semicolon") |
| #define TINT_CASTABLE_POP_DISABLE_WARNINGS() \ |
| static_assert(true, "require extra semicolon") |
| #endif |
| |
| TINT_CASTABLE_PUSH_DISABLE_WARNINGS(); |
| |
| namespace tint { |
| |
| // Forward declaration |
| class CastableBase; |
| |
| namespace detail { |
| template <typename T> |
| struct TypeInfoOf; |
| |
| } // namespace detail |
| |
| /// Helper macro to instantiate the TypeInfo<T> template for `CLASS`. |
| #define TINT_INSTANTIATE_TYPEINFO(CLASS) \ |
| TINT_CASTABLE_PUSH_DISABLE_WARNINGS(); \ |
| template <> \ |
| const tint::TypeInfo tint::detail::TypeInfoOf<CLASS>::info{ \ |
| &tint::detail::TypeInfoOf<CLASS::TrueBase>::info, \ |
| #CLASS, \ |
| tint::TypeInfo::HashCodeOf<CLASS>(), \ |
| tint::TypeInfo::CombinedHashCodeOf<CLASS>(), \ |
| }; \ |
| TINT_CASTABLE_POP_DISABLE_WARNINGS() |
| |
| /// Bit flags that can be passed to the template parameter `FLAGS` of Is() and |
| /// As(). |
| enum CastFlags { |
| /// Disables the static_assert() inside Is(), that compile-time-verifies that |
| /// the cast is possible. This flag may be useful for highly-generic template |
| /// code that needs to compile for template permutations that generate |
| /// impossible casts. |
| kDontErrorOnImpossibleCast = 1, |
| }; |
| |
| /// TypeInfo holds type information for a Castable type. |
| struct TypeInfo { |
| /// The type of a hash code |
| using HashCode = uint64_t; |
| |
| /// The base class of this type |
| const TypeInfo* base; |
| /// The type name |
| 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; |
| |
| /// @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 |
| // can quickly tell that that this TypeInfo does not derive from `type`. |
| if ((combined_hashcode & type->hashcode) != type->hashcode) { |
| return false; |
| } |
| |
| // Walk the base types, starting with this TypeInfo, to see if any of the |
| // pointers match `type`. |
| for (auto* ti = this; ti != nullptr; ti = ti->base) { |
| if (ti == type) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// @returns true if `type` derives from the class `TO` |
| /// @param type the object type to test from, which must be, or derive from |
| /// type `FROM`. |
| /// @see CastFlags |
| template <typename TO, typename FROM, int FLAGS = 0> |
| static inline bool Is(const tint::TypeInfo* type) { |
| constexpr const bool downcast = std::is_base_of<FROM, TO>::value; |
| constexpr const bool upcast = std::is_base_of<TO, FROM>::value; |
| constexpr const bool nocast = std::is_same<FROM, TO>::value; |
| constexpr const bool assert_is_castable = |
| (FLAGS & kDontErrorOnImpossibleCast) == 0; |
| |
| static_assert(upcast || downcast || nocast || !assert_is_castable, |
| "impossible cast"); |
| |
| if (upcast || nocast) { |
| return true; |
| } |
| |
| return type->Is(&Of<std::remove_const_t<TO>>()); |
| } |
| |
| /// @returns the static TypeInfo for the type T |
| template <typename T> |
| static const TypeInfo& Of() { |
| using NO_CV = typename std::remove_cv<T>::type; |
| return detail::TypeInfoOf<NO_CV>::info; |
| } |
| |
| /// @returns a compile-time hashcode for the type `T`. |
| /// @note the returned hashcode will have at most 2 bits set, as the hashes |
| /// are expected to be used in bloom-filters which will quickly saturate when |
| /// multiple hashcodes are bitwise-or'd together. |
| template <typename T> |
| static constexpr HashCode HashCodeOf() { |
| /// Use the compiler's "pretty" function name, which includes the template |
| /// type, to obtain a unique hash value. |
| #ifdef _MSC_VER |
| constexpr uint32_t crc = utils::CRC32(__FUNCSIG__); |
| #else |
| constexpr uint32_t crc = utils::CRC32(__PRETTY_FUNCTION__); |
| #endif |
| constexpr uint32_t bit_a = (crc & 63); |
| constexpr uint32_t bit_b = ((crc >> 6) & 63); |
| return (static_cast<HashCode>(1) << bit_a) | |
| (static_cast<HashCode>(1) << bit_b); |
| } |
| |
| /// @returns the hashcode of the given type, bitwise-or'd with the hashcodes |
| /// of all base classes. |
| template <typename T> |
| static constexpr HashCode CombinedHashCodeOf() { |
| if constexpr (std::is_same_v<T, CastableBase>) { |
| return HashCodeOf<CastableBase>(); |
| } else { |
| return HashCodeOf<T>() | CombinedHashCodeOf<typename T::TrueBase>(); |
| } |
| } |
| }; |
| |
| namespace detail { |
| |
| /// TypeInfoOf contains a single TypeInfo field for the type T. |
| /// TINT_INSTANTIATE_TYPEINFO() must be defined in a .cpp file for each type |
| /// `T`. |
| template <typename T> |
| struct TypeInfoOf { |
| /// The unique TypeInfo for the type T. |
| 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; |
| |
| } // namespace detail |
| |
| /// @returns true if `obj` is a valid pointer, and is of, or derives from the |
| /// class `TO` |
| /// @param obj the object to test from |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0, typename FROM = detail::Infer> |
| inline bool Is(FROM* obj) { |
| if (obj == nullptr) { |
| return false; |
| } |
| return TypeInfo::Is<TO, FROM, FLAGS>(&obj->TypeInfo()); |
| } |
| |
| /// @returns true if `obj` is a valid pointer, and is of, or derives from the |
| /// class `TO`, and pred(const TO*) 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`. |
| /// @see CastFlags |
| template <typename TO, |
| int FLAGS = 0, |
| typename FROM = 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)); |
| } |
| |
| /// @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) { |
| 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); |
| } |
| |
| /// @returns obj dynamically cast to the type `TO` or `nullptr` if |
| /// this object does not derive from `TO`. |
| /// @param obj the object to cast from |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0, typename FROM = detail::Infer> |
| inline TO* As(FROM* obj) { |
| auto* as_castable = static_cast<CastableBase*>(obj); |
| return Is<TO, FLAGS>(obj) ? static_cast<TO*>(as_castable) : nullptr; |
| } |
| |
| /// @returns obj dynamically cast to the type `TO` or `nullptr` if |
| /// this object does not derive from `TO`. |
| /// @param obj the object to cast from |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0, typename FROM = detail::Infer> |
| inline const TO* As(const FROM* obj) { |
| auto* as_castable = static_cast<const CastableBase*>(obj); |
| return Is<TO, FLAGS>(obj) ? static_cast<const TO*>(as_castable) : nullptr; |
| } |
| |
| /// CastableBase is the base class for all Castable objects. |
| /// It is not encouraged to directly derive from CastableBase without using the |
| /// Castable helper template. |
| /// @see Castable |
| class CastableBase { |
| public: |
| /// Copy constructor |
| CastableBase(const CastableBase&) = default; |
| |
| /// Destructor |
| virtual ~CastableBase() = default; |
| |
| /// Copy assignment |
| /// @param other the CastableBase to copy |
| /// @returns the new CastableBase |
| CastableBase& operator=(const CastableBase& other) = default; |
| |
| /// @returns the TypeInfo of the object |
| virtual const tint::TypeInfo& TypeInfo() const = 0; |
| |
| /// @returns true if this object is of, or derives from the class `TO` |
| template <typename TO> |
| inline bool Is() const { |
| return tint::Is<TO>(this); |
| } |
| |
| /// @returns true if this object is of, or derives from the class `TO` and |
| /// pred(const TO*) returns true |
| /// @param pred predicate function with signature `bool(const TO*)` called iff |
| /// object is of, or derives from the class `TO`. |
| template <typename TO, int FLAGS = 0, typename Pred = detail::Infer> |
| inline bool Is(Pred&& pred) const { |
| return tint::Is<TO, FLAGS>(this, std::forward<Pred>(pred)); |
| } |
| |
| /// @returns true if this object is of, or derives from any of the `TO` |
| /// classes. |
| template <typename... TO> |
| inline bool IsAnyOf() const { |
| return tint::IsAnyOf<TO...>(this); |
| } |
| |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if |
| /// this object does not derive from `TO`. |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0> |
| inline TO* As() { |
| return tint::As<TO, FLAGS>(this); |
| } |
| |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if |
| /// this object does not derive from `TO`. |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0> |
| inline const TO* As() const { |
| return tint::As<const TO, FLAGS>(this); |
| } |
| |
| protected: |
| CastableBase() = default; |
| }; |
| |
| /// Castable is a helper to derive `CLASS` from `BASE`, automatically |
| /// implementing the Is() and As() methods, along with a #Base type alias. |
| /// |
| /// Example usage: |
| /// |
| /// ``` |
| /// class Animal : public Castable<Animal> {}; |
| /// |
| /// class Sheep : public Castable<Sheep, Animal> {}; |
| /// |
| /// Sheep* cast_to_sheep(Animal* animal) { |
| /// // You can query whether a Castable is of the given type with Is<T>(): |
| /// printf("animal is a sheep? %s", animal->Is<Sheep>() ? "yes" : "no"); |
| /// |
| /// // You can always just try the cast with As<T>(). |
| /// // If the object is not of the correct type, As<T>() will return nullptr: |
| /// return animal->As<Sheep>(); |
| /// } |
| /// ``` |
| template <typename CLASS, typename BASE = CastableBase> |
| class Castable : public BASE { |
| public: |
| // Inherit the `BASE` class constructors. |
| using BASE::BASE; |
| |
| /// A type alias for `CLASS` to easily access the `BASE` class members. |
| /// Base actually aliases to the Castable instead of `BASE` so that you can |
| /// use Base in the `CLASS` constructor. |
| using Base = Castable; |
| |
| /// A type alias for `BASE`. |
| using TrueBase = BASE; |
| |
| /// @returns the TypeInfo of the object |
| const tint::TypeInfo& TypeInfo() const override { |
| return TypeInfo::Of<CLASS>(); |
| } |
| |
| /// @returns true if this object is of, or derives from the class `TO` |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0> |
| inline bool Is() const { |
| return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this)); |
| } |
| |
| /// @returns true if this object is of, or derives from the class `TO` and |
| /// pred(const TO*) returns true |
| /// @param pred predicate function with signature `bool(const TO*)` called iff |
| /// object is of, or derives from the class `TO`. |
| template <int FLAGS = 0, typename Pred = detail::Infer> |
| inline bool Is(Pred&& pred) const { |
| using TO = |
| typename std::remove_pointer<traits::ParameterType<Pred, 0>>::type; |
| return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this), |
| std::forward<Pred>(pred)); |
| } |
| |
| /// @returns true if this object is of, or derives from any of the `TO` |
| /// classes. |
| template <typename... TO> |
| inline bool IsAnyOf() const { |
| return tint::IsAnyOf<TO...>(static_cast<const CLASS*>(this)); |
| } |
| |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if |
| /// this object does not derive from `TO`. |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0> |
| inline TO* As() { |
| return tint::As<TO, FLAGS>(this); |
| } |
| |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if |
| /// this object does not derive from `TO`. |
| /// @see CastFlags |
| template <typename TO, int FLAGS = 0> |
| inline const TO* As() const { |
| return tint::As<const TO, FLAGS>(this); |
| } |
| }; |
| |
| 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(); |
| |
| #endif // SRC_CASTABLE_H_ |