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