| // 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 <cstdint> | |
| #include <type_traits> | |
| #include <utility> | |
| namespace tint { | |
| /// Helper macro to instantiate the ClassID for `CLASS`. | |
| #define TINT_INSTANTIATE_CLASS_ID(CLASS) \ | |
| template <> \ | |
| const char ::tint::ClassID::Unique<CLASS>::token = 0 | |
| /// ClassID represents a unique, comparable identifier for a C++ type. | |
| class ClassID { | |
| private: | |
| /// Helper template that holds a single static field, which is used by Of() | |
| /// to obtain a unique identifier by taking the field's address. | |
| template <typename T> | |
| struct Unique { | |
| static const char token; | |
| }; | |
| public: | |
| /// @returns the unique ClassID for the type T. | |
| template <typename T> | |
| static ClassID Of() { | |
| // Take the address of a static variable to produce a unique identifier for | |
| // the type T. | |
| return ClassID{reinterpret_cast<uintptr_t>(&Unique<T>::token)}; | |
| } | |
| /// Equality operator | |
| /// @param rhs the ClassID to compare against | |
| /// @returns true if this ClassID is equal to `rhs` | |
| inline bool operator==(ClassID& rhs) const { return id == rhs.id; } | |
| private: | |
| inline explicit ClassID(uintptr_t v) : id(v) {} | |
| const uintptr_t id; | |
| }; | |
| /// 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: | |
| /// Move constructor | |
| CastableBase(CastableBase&&) = default; | |
| virtual ~CastableBase() = default; | |
| /// @returns true if this object is of, or derives from a class with the | |
| /// ClassID `id`. | |
| /// @param id the ClassID to test for | |
| virtual bool Is(ClassID id) const; | |
| /// @returns true if this object is of, or derives from the class `TO` | |
| template <typename TO> | |
| bool Is() const { | |
| using FROM = CastableBase; | |
| 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; | |
| static_assert(upcast || downcast || nocast, "impossible cast"); | |
| if (upcast || nocast) { | |
| return true; | |
| } | |
| return Is(ClassID::Of<TO>()); | |
| } | |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if | |
| /// this object does not derive from `TO`. | |
| template <typename TO> | |
| inline TO* As() { | |
| return Is<TO>() ? static_cast<TO*>(this) : nullptr; | |
| } | |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if | |
| /// this object does not derive from `TO`. | |
| template <typename TO> | |
| inline const TO* As() const { | |
| return Is<TO>() ? static_cast<const TO*>(this) : nullptr; | |
| } | |
| 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; | |
| /// @returns true if this object is of, or derives from a class with the | |
| /// ClassID `id`. | |
| /// @param id the ClassID to test for | |
| bool Is(ClassID id) const override { | |
| return ClassID::Of<CLASS>() == id || BASE::Is(id); | |
| } | |
| /// @returns true if this object is of, or derives from the class `TO` | |
| template <typename TO> | |
| bool Is() const { | |
| using FROM = Castable; | |
| 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; | |
| static_assert(upcast || downcast || nocast, "impossible cast"); | |
| if (upcast || nocast) { | |
| return true; | |
| } | |
| return Is(ClassID::Of<TO>()); | |
| } | |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if | |
| /// this object does not derive from `TO`. | |
| template <typename TO> | |
| inline TO* As() { | |
| return Is<TO>() ? static_cast<TO*>(this) : nullptr; | |
| } | |
| /// @returns this object dynamically cast to the type `TO` or `nullptr` if | |
| /// this object does not derive from `TO`. | |
| template <typename TO> | |
| inline const TO* As() const { | |
| return Is<TO>() ? static_cast<const TO*>(this) : nullptr; | |
| } | |
| }; | |
| /// As() dynamically casts `obj` to the target type `TO`. | |
| /// @returns the cast object, or nullptr if `obj` is `nullptr` or not of the | |
| /// type `TO`. | |
| /// @param obj the object to cast | |
| template <typename TO, typename FROM> | |
| inline TO* As(FROM* obj) { | |
| if (obj == nullptr) { | |
| return nullptr; | |
| } | |
| return obj->template As<TO>(); | |
| } | |
| } // namespace tint | |
| #endif // SRC_CASTABLE_H_ |