P4C
The P4 Compiler
Loading...
Searching...
No Matches
rtti.h
1/*
2Licensed under the Apache License, Version 2.0 (the "License");
3you may not use this file except in compliance with the License.
4You may obtain a copy of the License at
5
6 http://www.apache.org/licenses/LICENSE-2.0
7
8Unless required by applicable law or agreed to in writing, software
9distributed under the License is distributed on an "AS IS" BASIS,
10WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11See the License for the specific language governing permissions and
12limitations under the License.
13*/
14
15#ifndef LIB_RTTI_H_
16#define LIB_RTTI_H_
17
18#include <algorithm>
19#include <array>
20#include <cstdint>
21#include <type_traits>
22
65
66namespace P4::RTTI {
67
68using TypeId = std::uint64_t;
69static constexpr TypeId InvalidTypeId = UINT64_C(0);
70static constexpr unsigned kDiscriminatorBits = 56;
71static constexpr uint64_t kInnerTypeIdMask = (UINT64_C(1) << kDiscriminatorBits) - 1;
72static constexpr uint64_t kHashDiscriminator = UINT64_C(0xFF);
73
74namespace detail {
75// Apparently string_view does not optimize properly in constexpr context
76// for GCC < 13
78 template <size_t N>
79 constexpr TypeNameHolder(const char (&str)[N]) : str(&str[0]), length(N - 1) {} // NOLINT
80
81 const char *str;
82 size_t length;
83};
84
85// Derive the string containing the ultimate typename of `T`. The resulting
86// string should be available in constexpr context.
87// TODO: Probably use std::source_location::function_name with C++20
88template <typename T>
89constexpr TypeNameHolder typeNameProxy() {
90#ifdef __clang__
91 return __PRETTY_FUNCTION__;
92#elif defined(__GNUC__)
93 return __PRETTY_FUNCTION__;
94#else
95// TODO: We might use __FUNCSIG__ with MSVC should this will be necessary.
96#error "Unsupported compiler"
97#endif
98}
99
100static constexpr uint64_t FNV1a(const char *str, size_t n,
101 uint64_t hash = UINT64_C(0xcbf29ce484222325)) {
102 return n == 0 ? hash : FNV1a(str + 1, n - 1, UINT64_C(0x100000001b3) * (hash ^ str[0]));
103}
104
105static constexpr uint64_t FNV1a(TypeNameHolder str) { return FNV1a(str.str, str.length); }
106
107// TypeIdResolver provides a unified way to getting typeid of the type `T`. Typeid
108// could be derived in two different ways:
109// * Explicitly, if `T` implements a `T::static_typeId()` method that could be
110// used in a constexpr context
111// * Implicitly, via hash from typename itself
112// Also, the explicit specification could fallback to an implicit one, of the
113// returned typeid is `InvalidTypeId`
114//
115// The top 8 bits of typeid are reserved as discriminator and could be used to
116// delineate different typeids. Currently the following discriminators are reserved:
117// - 0xFF is used to mark all hash-derived typeids
118// - 0x00 is used to mark all explicitly-specified typeids
119// In addition, IR::Node hierarchy uses the following discriminators:
120// - 0x01 for Vector<T> with low 56 bits contain typeid of `T`.
121// - 0x02 for IndexedVector<T> with low 56 bits contain typeid of `T`.
122// One could use DECLARE_TYPEINFO_WITH_NESTED_TYPEID to combine typeids in such way.
123
124// Default implementation of typeid resolver: use hash derived from type name,
125// but mark upper 8 bits to be 0xFF
126template <typename T, typename = void>
128 static constexpr TypeId resolve() noexcept {
129 // Get original 64-bit hash
130 uint64_t hash = FNV1a(typeNameProxy<T>());
131 // Xor-fold to obtain 56-bit value
132 hash = (hash >> kDiscriminatorBits) ^ (hash & kInnerTypeIdMask);
133 return (kHashDiscriminator << kDiscriminatorBits) | hash;
134 }
135};
136
137// Specialized implementation that looks for static_typeId member.
138template <typename T>
139struct TypeIdResolver<T, std::void_t<decltype(T::static_typeId)>> {
140 static constexpr uint64_t resolve() noexcept {
141 constexpr TypeId id = T::static_typeId();
142 // Allow fall-back implementation if class does not want to explicitly
143 // specify a typeid
144 if constexpr (id == InvalidTypeId) return TypeIdResolver<T, int>::resolve();
145
146 return id;
147 }
148};
149
150} // namespace detail
151
153static constexpr TypeId innerTypeId(TypeId id) { return id & kInnerTypeIdMask; }
154
156static constexpr TypeId typeidDiscriminator(TypeId id) { return id >> kDiscriminatorBits; }
157
159static constexpr TypeId combineTypeIdWithDiscriminator(TypeId discriminator, TypeId id) {
160 return (discriminator << kDiscriminatorBits) | innerTypeId(id);
161}
162
163struct Base;
164
165template <typename This, typename... Parents>
166struct TypeInfo {
167 using T = std::remove_const_t<std::remove_reference_t<This>>;
168
169 static_assert((... && std::is_base_of_v<Parents, This>),
170 "One or more parents are not a base of this type.");
171
172 static_assert((... && std::is_base_of_v<Base, Parents>),
173 "One or more parent hierarchies is not based on top of RTTI::Base.");
174
175 static_assert(std::is_base_of_v<Base, This>,
176 "Invalid typeinfo requested, class is not based on top of RTTI::Base.");
177
178 static_assert(
179 std::is_same_v<T *, decltype(T::rttiEnabledMarker(nullptr))>,
180 "Invalid typeinfo requested, class does not declare typeinfo via DECLARE_TYPEINFO.");
181
182 [[nodiscard]] static constexpr TypeId id() noexcept {
184 }
185
186 [[nodiscard]] static constexpr bool isA(TypeId typeId) noexcept {
187 return (id() == typeId) || (... || (Parents::TypeInfo::isA(typeId)));
188 }
189
190 template <typename T>
191 [[nodiscard]] static const void *dyn_cast(TypeId typeId, const T *ptr) noexcept {
192 if (id() == typeId) return static_cast<const This *>(ptr);
193
194 std::array<const void *, sizeof...(Parents)> ptrs = {
195 Parents::TypeInfo::dyn_cast(typeId, ptr)...};
196
197 auto it =
198 std::find_if(ptrs.begin(), ptrs.end(), [](const void *ptr) { return ptr != nullptr; });
199 return (it != ptrs.end()) ? *it : nullptr;
200 }
201};
202
203struct Base {
204 virtual ~Base() = default;
205
208 [[nodiscard]] virtual TypeId typeId() const noexcept = 0;
211 [[nodiscard]] virtual bool isA(TypeId typeId) const noexcept = 0;
212
215 template <typename T>
216 [[nodiscard]] bool is() const noexcept {
217 return isA(TypeInfo<T>::id());
218 }
219
225 template <typename T>
226 [[nodiscard]] T *to() noexcept {
227 return reinterpret_cast<T *>(const_cast<void *>(toImpl(TypeInfo<T>::id())));
228 }
229
231 template <typename T>
232 [[nodiscard]] const T *to() const noexcept {
233 return reinterpret_cast<const T *>(toImpl(TypeInfo<T>::id()));
234 }
235
236 protected:
237 [[nodiscard]] virtual const void *toImpl(TypeId typeId) const noexcept = 0;
238};
239
240} // namespace P4::RTTI
241
259#define DECLARE_TYPEINFO(T, ...) \
260 private: \
261 static constexpr P4::RTTI::TypeId static_typeId() { return P4::RTTI::InvalidTypeId; } \
262 DECLARE_TYPEINFO_COMMON(T, ##__VA_ARGS__)
263// static_typeId is private above in order to hide explicit typeId from the base
264// class, as in e.g.
265// class A {
266// DECLARE_TYPEINFO_WITH_TYPEID(A, 42);
267// };
268// class B : public A {
269// DECLARE_TYPEINFO(A, B);
270// };
271// We need to ensure static_typeId is not visible inside `B`, otherwise
272// TypeIdResolver will be confused.
273
280#define DECLARE_TYPEINFO_WITH_TYPEID(T, Id, ...) \
281 public: \
282 static constexpr P4::RTTI::TypeId static_typeId() { return P4::RTTI::TypeId(Id); } \
283 DECLARE_TYPEINFO_COMMON(T, ##__VA_ARGS__)
284
291#define DECLARE_TYPEINFO_WITH_DISCRIMINATOR(T, Discriminator, InnerT, ...) \
292 public: \
293 static constexpr P4::RTTI::TypeId static_typeId() { \
294 return P4::RTTI::combineTypeIdWithDiscriminator(P4::RTTI::TypeId(Discriminator), \
295 P4::RTTI::TypeInfo<InnerT>::id()); \
296 }; \
297 DECLARE_TYPEINFO_COMMON(T, ##__VA_ARGS__)
298
299#define DECLARE_TYPEINFO_COMMON(T, ...) \
300 public: \
301 static constexpr T *rttiEnabledMarker(T *); \
302 using TypeInfo = P4::RTTI::TypeInfo<T, ##__VA_ARGS__>; \
303 [[nodiscard]] P4::RTTI::TypeId typeId() const noexcept override { return TypeInfo::id(); } \
304 [[nodiscard]] bool isA(P4::RTTI::TypeId typeId) const noexcept override { \
305 return TypeInfo::isA(typeId); \
306 } \
307 \
308 protected: \
309 [[nodiscard]] const void *toImpl(P4::RTTI::TypeId typeId) const noexcept override { \
310 return TypeInfo::isA(typeId) ? TypeInfo::dyn_cast(typeId, this) : nullptr; \
311 }
312
313#endif /* LIB_RTTI_H_ */
STL namespace.
Definition rtti.h:203
T * to() noexcept
Definition rtti.h:226
bool is() const noexcept
Definition rtti.h:216
virtual TypeId typeId() const noexcept=0
const T * to() const noexcept
Same as to, but returns const pointer to T.
Definition rtti.h:232
virtual bool isA(TypeId typeId) const noexcept=0
Definition rtti.h:166