| // 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_ |