• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SRC_CASTABLE_H_
16 #define SRC_CASTABLE_H_
17 
18 #include <utility>
19 
20 #include "src/traits.h"
21 
22 #if defined(__clang__)
23 /// Temporarily disable certain warnings when using Castable API
24 #define TINT_CASTABLE_PUSH_DISABLE_WARNINGS()                               \
25   _Pragma("clang diagnostic push")                                     /**/ \
26       _Pragma("clang diagnostic ignored \"-Wundefined-var-template\"") /**/ \
27       static_assert(true, "require extra semicolon")
28 
29 /// Restore disabled warnings
30 #define TINT_CASTABLE_POP_DISABLE_WARNINGS() \
31   _Pragma("clang diagnostic pop") /**/       \
32       static_assert(true, "require extra semicolon")
33 #else
34 #define TINT_CASTABLE_PUSH_DISABLE_WARNINGS() \
35   static_assert(true, "require extra semicolon")
36 #define TINT_CASTABLE_POP_DISABLE_WARNINGS() \
37   static_assert(true, "require extra semicolon")
38 #endif
39 
40 TINT_CASTABLE_PUSH_DISABLE_WARNINGS();
41 
42 namespace tint {
43 
44 namespace detail {
45 template <typename T>
46 struct TypeInfoOf;
47 }  // namespace detail
48 
49 // Forward declaration
50 class CastableBase;
51 
52 /// Helper macro to instantiate the TypeInfo<T> template for `CLASS`.
53 #define TINT_INSTANTIATE_TYPEINFO(CLASS)                      \
54   TINT_CASTABLE_PUSH_DISABLE_WARNINGS();                      \
55   template <>                                                 \
56   const tint::TypeInfo tint::detail::TypeInfoOf<CLASS>::info{ \
57       &tint::detail::TypeInfoOf<CLASS::TrueBase>::info,       \
58       #CLASS,                                                 \
59   };                                                          \
60   TINT_CASTABLE_POP_DISABLE_WARNINGS()
61 
62 /// TypeInfo holds type information for a Castable type.
63 struct TypeInfo {
64   /// The base class of this type.
65   const TypeInfo* base;
66   /// The type name
67   const char* name;
68 
69   /// @param type the test type info
70   /// @returns true if the class with this TypeInfo is of, or derives from the
71   /// class with the given TypeInfo.
72   bool Is(const tint::TypeInfo& type) const;
73 
74   /// @returns the static TypeInfo for the type T
75   template <typename T>
OfTypeInfo76   static const TypeInfo& Of() {
77     using NO_CV = typename std::remove_cv<T>::type;
78     return detail::TypeInfoOf<NO_CV>::info;
79   }
80 };
81 
82 namespace detail {
83 
84 /// TypeInfoOf contains a single TypeInfo field for the type T.
85 /// TINT_INSTANTIATE_TYPEINFO() must be defined in a .cpp file for each type
86 /// `T`.
87 template <typename T>
88 struct TypeInfoOf {
89   /// The unique TypeInfo for the type T.
90   static const TypeInfo info;
91 };
92 
93 // Forward declaration
94 template <typename TO_FIRST, typename... TO_REST>
95 struct IsAnyOf;
96 
97 /// A placeholder structure used for template parameters that need a default
98 /// type, but can always be automatically inferred.
99 struct Infer;
100 
101 }  // namespace detail
102 
103 /// Bit flags that can be passed to the template parameter `FLAGS` of Is() and
104 /// As().
105 enum CastFlags {
106   /// Disables the static_assert() inside Is(), that compile-time-verifies that
107   /// the cast is possible. This flag may be useful for highly-generic template
108   /// code that needs to compile for template permutations that generate
109   /// impossible casts.
110   kDontErrorOnImpossibleCast = 1,
111 };
112 
113 /// @returns true if `obj` is a valid pointer, and is of, or derives from the
114 /// class `TO`
115 /// @param obj the object to test from
116 /// @see CastFlags
117 template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
Is(FROM * obj)118 inline bool Is(FROM* obj) {
119   constexpr const bool downcast = std::is_base_of<FROM, TO>::value;
120   constexpr const bool upcast = std::is_base_of<TO, FROM>::value;
121   constexpr const bool nocast = std::is_same<FROM, TO>::value;
122   constexpr const bool assert_is_castable =
123       (FLAGS & kDontErrorOnImpossibleCast) == 0;
124 
125   static_assert(upcast || downcast || nocast || !assert_is_castable,
126                 "impossible cast");
127 
128   if (obj == nullptr) {
129     return false;
130   }
131 
132   if (upcast || nocast) {
133     return true;
134   }
135 
136   return obj->TypeInfo().Is(TypeInfo::Of<std::remove_const_t<TO>>());
137 }
138 
139 /// @returns true if `obj` is a valid pointer, and is of, or derives from the
140 /// class `TO`, and pred(const TO*) returns true
141 /// @param obj the object to test from
142 /// @param pred predicate function with signature `bool(const TO*)` called iff
143 /// object is of, or derives from the class `TO`.
144 /// @see CastFlags
145 template <typename TO,
146           int FLAGS = 0,
147           typename FROM = detail::Infer,
148           typename Pred = detail::Infer>
Is(FROM * obj,Pred && pred)149 inline bool Is(FROM* obj, Pred&& pred) {
150   constexpr const bool downcast = std::is_base_of<FROM, TO>::value;
151   constexpr const bool upcast = std::is_base_of<TO, FROM>::value;
152   constexpr const bool nocast = std::is_same<FROM, TO>::value;
153   static_assert(upcast || downcast || nocast, "impossible cast");
154 
155   if (obj == nullptr) {
156     return false;
157   }
158 
159   bool is_type = upcast || nocast ||
160                  obj->TypeInfo().Is(TypeInfo::Of<std::remove_const_t<TO>>());
161 
162   return is_type && pred(static_cast<std::add_const_t<TO>*>(obj));
163 }
164 
165 /// @returns true if `obj` is of, or derives from any of the `TO`
166 /// classes.
167 /// @param obj the object to cast from
168 template <typename... TO, typename FROM>
IsAnyOf(FROM * obj)169 inline bool IsAnyOf(FROM* obj) {
170   return detail::IsAnyOf<TO...>::Exec(obj);
171 }
172 
173 /// @returns obj dynamically cast to the type `TO` or `nullptr` if
174 /// this object does not derive from `TO`.
175 /// @param obj the object to cast from
176 /// @see CastFlags
177 template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
As(FROM * obj)178 inline TO* As(FROM* obj) {
179   auto* as_castable = static_cast<CastableBase*>(obj);
180   return Is<TO, FLAGS>(obj) ? static_cast<TO*>(as_castable) : nullptr;
181 }
182 
183 /// @returns obj dynamically cast to the type `TO` or `nullptr` if
184 /// this object does not derive from `TO`.
185 /// @param obj the object to cast from
186 /// @see CastFlags
187 template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
As(const FROM * obj)188 inline const TO* As(const FROM* obj) {
189   auto* as_castable = static_cast<const CastableBase*>(obj);
190   return Is<TO, FLAGS>(obj) ? static_cast<const TO*>(as_castable) : nullptr;
191 }
192 
193 /// CastableBase is the base class for all Castable objects.
194 /// It is not encouraged to directly derive from CastableBase without using the
195 /// Castable helper template.
196 /// @see Castable
197 class CastableBase {
198  public:
199   /// Copy constructor
200   CastableBase(const CastableBase&) = default;
201 
202   /// Destructor
203   virtual ~CastableBase() = default;
204 
205   /// Copy assignment
206   /// @param other the CastableBase to copy
207   /// @returns the new CastableBase
208   CastableBase& operator=(const CastableBase& other) = default;
209 
210   /// @returns the TypeInfo of the object
211   virtual const tint::TypeInfo& TypeInfo() const = 0;
212 
213   /// @returns true if this object is of, or derives from the class `TO`
214   template <typename TO>
Is()215   inline bool Is() const {
216     return tint::Is<TO>(this);
217   }
218 
219   /// @returns true if this object is of, or derives from the class `TO` and
220   /// pred(const TO*) returns true
221   /// @param pred predicate function with signature `bool(const TO*)` called iff
222   /// object is of, or derives from the class `TO`.
223   template <typename TO, int FLAGS = 0, typename Pred = detail::Infer>
Is(Pred && pred)224   inline bool Is(Pred&& pred) const {
225     return tint::Is<TO, FLAGS>(this, std::forward<Pred>(pred));
226   }
227 
228   /// @returns true if this object is of, or derives from any of the `TO`
229   /// classes.
230   template <typename... TO>
IsAnyOf()231   inline bool IsAnyOf() const {
232     return tint::IsAnyOf<TO...>(this);
233   }
234 
235   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
236   /// this object does not derive from `TO`.
237   /// @see CastFlags
238   template <typename TO, int FLAGS = 0>
As()239   inline TO* As() {
240     return tint::As<TO, FLAGS>(this);
241   }
242 
243   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
244   /// this object does not derive from `TO`.
245   /// @see CastFlags
246   template <typename TO, int FLAGS = 0>
As()247   inline const TO* As() const {
248     return tint::As<const TO, FLAGS>(this);
249   }
250 
251  protected:
252   CastableBase() = default;
253 };
254 
255 /// Castable is a helper to derive `CLASS` from `BASE`, automatically
256 /// implementing the Is() and As() methods, along with a #Base type alias.
257 ///
258 /// Example usage:
259 ///
260 /// ```
261 /// class Animal : public Castable<Animal> {};
262 ///
263 /// class Sheep : public Castable<Sheep, Animal> {};
264 ///
265 /// Sheep* cast_to_sheep(Animal* animal) {
266 ///    // You can query whether a Castable is of the given type with Is<T>():
267 ///    printf("animal is a sheep? %s", animal->Is<Sheep>() ? "yes" : "no");
268 ///
269 ///    // You can always just try the cast with As<T>().
270 ///    // If the object is not of the correct type, As<T>() will return nullptr:
271 ///    return animal->As<Sheep>();
272 /// }
273 /// ```
274 template <typename CLASS, typename BASE = CastableBase>
275 class Castable : public BASE {
276  public:
277   // Inherit the `BASE` class constructors.
278   using BASE::BASE;
279 
280   /// A type alias for `CLASS` to easily access the `BASE` class members.
281   /// Base actually aliases to the Castable instead of `BASE` so that you can
282   /// use Base in the `CLASS` constructor.
283   using Base = Castable;
284 
285   /// A type alias for `BASE`.
286   using TrueBase = BASE;
287 
288   /// @returns the TypeInfo of the object
TypeInfo()289   const tint::TypeInfo& TypeInfo() const override {
290     return TypeInfo::Of<CLASS>();
291   }
292 
293   /// @returns true if this object is of, or derives from the class `TO`
294   /// @see CastFlags
295   template <typename TO, int FLAGS = 0>
Is()296   inline bool Is() const {
297     return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this));
298   }
299 
300   /// @returns true if this object is of, or derives from the class `TO` and
301   /// pred(const TO*) returns true
302   /// @param pred predicate function with signature `bool(const TO*)` called iff
303   /// object is of, or derives from the class `TO`.
304   template <int FLAGS = 0, typename Pred = detail::Infer>
Is(Pred && pred)305   inline bool Is(Pred&& pred) const {
306     using TO =
307         typename std::remove_pointer<traits::ParameterType<Pred, 0>>::type;
308     return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this),
309                                std::forward<Pred>(pred));
310   }
311 
312   /// @returns true if this object is of, or derives from any of the `TO`
313   /// classes.
314   template <typename... TO>
IsAnyOf()315   inline bool IsAnyOf() const {
316     return tint::IsAnyOf<TO...>(static_cast<const CLASS*>(this));
317   }
318 
319   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
320   /// this object does not derive from `TO`.
321   /// @see CastFlags
322   template <typename TO, int FLAGS = 0>
As()323   inline TO* As() {
324     return tint::As<TO, FLAGS>(this);
325   }
326 
327   /// @returns this object dynamically cast to the type `TO` or `nullptr` if
328   /// this object does not derive from `TO`.
329   /// @see CastFlags
330   template <typename TO, int FLAGS = 0>
As()331   inline const TO* As() const {
332     return tint::As<const TO, FLAGS>(this);
333   }
334 };
335 
336 namespace detail {
337 /// Helper for Castable::IsAnyOf
338 template <typename TO_FIRST, typename... TO_REST>
339 struct IsAnyOf {
340   /// @param obj castable object to test
341   /// @returns true if `obj` is of, or derives from any of `[TO_FIRST,
342   /// ...TO_REST]`
343   template <typename FROM>
ExecIsAnyOf344   static bool Exec(FROM* obj) {
345     return Is<TO_FIRST>(obj) || IsAnyOf<TO_REST...>::Exec(obj);
346   }
347 };
348 /// Terminal specialization
349 template <typename TO>
350 struct IsAnyOf<TO> {
351   /// @param obj castable object to test
352   /// @returns true if `obj` is of, or derives from TO
353   template <typename FROM>
354   static bool Exec(FROM* obj) {
355     return Is<TO>(obj);
356   }
357 };
358 }  // namespace detail
359 
360 }  // namespace tint
361 
362 TINT_CASTABLE_POP_DISABLE_WARNINGS();
363 
364 #endif  // SRC_CASTABLE_H_
365