cpp-reflect
C++ Reflection and Annotations Library
Loading...
Searching...
No Matches
type_info.cppm
Go to the documentation of this file.
1// Copyright (c) 2024-2025, Víctor Castillo Agüero.
2// SPDX-License-Identifier: GPL-3.0-or-later
3
8
9export module reflect:type_info;
10
11export import std;
12
13export import :types;
14export import :type_name;
15export import :accessors;
16export import :equality;
17
18namespace refl {
19 export class type_info;
20 std::map<type_id_t, type_info> type_registry {};
21
22 type_id_t get_id_from_info_getter(const type_info& (*tif)());
23
24 export struct field_info {
25 std::size_t index;
26 std::string name;
27 std::size_t size;
28 std::size_t offset;
31 [[refl::ignore]]
32 const type_info& (*type)();
33 std::vector<std::pair<const type_info& (*)(), void*>> metadata;
34
35 // Accessors
36 void* get_ptr(void* obj) const {
37 return static_cast<char *>(obj) + offset;
38 }
39
40 template <typename T>
41 T& get_ref(void* obj) const {
42 return *static_cast<T *>(get_ptr(obj));
43 }
44
45 template <typename MetadataType>
46 bool has_metadata() const {
47 static constexpr type_id_t t_id = refl::type_id<MetadataType>;
48 for (const auto & [tif, ptr] : metadata) {
49 if (t_id == get_id_from_info_getter(tif)) {
50 return true;
51 }
52 }
53 return false;
54 }
55
56 template <typename MetadataType>
57 const MetadataType& get_metadata() const {
58 static constexpr type_id_t t_id = refl::type_id<MetadataType>;
59 for (const auto & [tif, ptr] : metadata) {
60 if (t_id == get_id_from_info_getter(tif)) {
61 return *static_cast<const MetadataType*>(ptr);
62 }
63 }
64 throw std::runtime_error("Could not find metadata type");
65 }
66 };
67
68 export struct field_path {
69 friend std::hash<refl::field_path>;
70
71 field_path(const field_info* field): fields_{field} { }
72 field_path(std::initializer_list<const field_info*> fields): fields_(fields) { }
73
74 const type_info& type() const {
75 return fields_.back()->type();
76 }
77
78 void* get_ptr(void* obj) const {
79 void* ptr = obj;
80
81 for (const field_info* field : fields_) {
82 ptr = field->get_ptr(ptr);
83 }
84
85 return ptr;
86 }
87
88 template <typename T>
89 T& get_ref(void* obj) const {
90 return *static_cast<T *>(get_ptr(obj));
91 }
92
93 bool operator==(const field_path& other) const {
94 return fields_ == other.fields_;
95 }
96 private:
97 std::vector<const field_info*> fields_;
98 };
99
100 export struct method_info {
101 std::size_t index;
102 std::string name;
104 };
105
106 export template <typename T>
108 static std::vector<type_id_t> vector() {
109 return {};
110 }
111 };
112 template <template <typename...> typename Pack, typename... Args>
113 struct get_pack_param_ids<Pack<Args...>> {
114 static std::vector<type_id_t> vector();
115 };
116 template <template <typename, std::size_t> typename Pack, typename T, std::size_t I>
117 struct get_pack_param_ids<Pack<T, I>> {
118 static std::vector<type_id_t> vector();
119 };
120
121 class type_info {
122 private:
123 template <typename Type>
124 static const type_info& type_getter() {
125 return from<Type>();
126 }
127
128 template <typename Field>
129 static field_info make_field_data() {
131 .index = Field::index,
132 .name = Field::name,
133 .size = Field::size,
134 .offset = Field::offset,
135 .access_type = Field::access,
136 .type_id = Field::type_id,
137 .type = &type_getter<typename Field::type>,
138 };
139 field.metadata.reserve(Field::metadata_count);
140
141 [&]<std::size_t... I>(std::index_sequence<I...>) {
142 (field.metadata.emplace_back( //
143 &type_getter<typename Field::template metadata_type<I> >,
144 static_cast<void*>(new std::remove_const_t<typename Field::template metadata_type<I>>{Field::template metadata_item<I>})), ...);
145 }(std::make_index_sequence<Field::metadata_count>{});
146
147 return field;
148 }
149
150 template <typename Field>
151 void push_field() {
152 fields_.push_back(make_field_data<Field>());
153 const auto& field = fields_.back();
154
155 fields_by_name_[field.name] = &field;
156 fields_by_offset_[field.offset] = &field;
157 }
158
159 template <typename Method>
160 void push_method() {
161 methods_.push_back({
162 // .type = []() -> type_info { return from<typename field<T, I>::type>(); },
163 .index = Method::index,
164 .name = Method::name,
165 .access_type = Method::access,
166 });
167 const auto& method = methods_.back();
168
169 methods_by_name_[method.name] = &method;
170 }
171 public:
172
173 template <typename T>
174 static const type_info& from() {
175 using type = std::remove_const_t<std::remove_reference_t<T>>;
176 static constexpr type_id_t tid = type_id<type>;
177 static constexpr type_id_t pid = pack_type_id<type>;
178
179 if (type_registry.contains(tid)) {
180 return type_registry[tid];
181 }
182
183 type_registry.emplace(tid, type_info{});
184 type_info &ti = type_registry.at(tid);
185 if constexpr (Reflected<type>) {
186 static constexpr std::size_t f_count = field_count<type>;
187 static constexpr std::size_t m_count = method_count<type>;
188
189 [&]<std::size_t... I>(std::index_sequence<I...>) {
190 (ti.push_field<field<type, I>>(), ...);
191 }(std::make_index_sequence<f_count>{});
192
193 [&]<std::size_t... I>(std::index_sequence<I...>) {
194 (ti.push_method<method<type, I>>(), ...);
195 }(std::make_index_sequence<m_count>{});
196
197 ti.type_id_ = tid;
198 ti.name_ = type_name<T>;
199 } else {
200 ti.type_id_ = tid;
201 ti.name_ = type_name<T>;
202 }
203
204 if constexpr (std::is_const_v<T>) {
205 ti.is_const_ = true;
206 }
207
208 if constexpr (std::is_lvalue_reference_v<T>) {
209 ti.is_lval_ref_ = true;
210 ti.indirect_type_id_ = from<std::remove_reference_t<T>>().id();
211 } else if constexpr (std::is_rvalue_reference_v<T>) {
212 ti.is_rval_ref_ = true;
213 ti.indirect_type_id_ = from<std::remove_reference_t<T>>().id();
214 } else if constexpr (std::is_pointer_v<T>) {
215 ti.is_ptr_ = true;
216 ti.indirect_type_id_ = from<std::remove_pointer_t<T>>().id();
217 }
218
219 if constexpr (pid != 0) {
220 ti.pack_id_ = pid;
221 ti.pack_param_ids_ = get_pack_param_ids<type>::vector();
222 }
223
224 if constexpr(std::is_copy_constructible_v<type>) {
225 ti.copy_construct_function_ = [](const void* src) -> void* {
226 const type& src_ref = *static_cast<const type*>(src);
227 type* dest = new type(src_ref);
228 return dest;
229 };
230 }
231 if constexpr(std::is_copy_assignable_v<type>) {
232 ti.copy_assign_function_ = [](void* dest, const void* src) {
233 type& dest_ref = *static_cast<type*>(dest);
234 const type& src_ref = *static_cast<const type*>(src);
235 dest_ref = src_ref;
236 };
237 }
238 if constexpr(Reflected<type>) {
239 ti.equality_function_ = [](const void* lhs, const void* rhs) {
240 const type& LHS = *static_cast<const type*>(lhs);
241 const type& RHS = *static_cast<const type*>(rhs);
242 return deep_eq(LHS, RHS);
243 };
244 } else if constexpr (std::equality_comparable<type> and not std::is_function_v<type>) {
245 ti.equality_function_ = [](const void* lhs, const void* rhs) {
246 const type& LHS = *static_cast<const type*>(lhs);
247 const type& RHS = *static_cast<const type*>(rhs);
248 return LHS == RHS;
249 };
250 }
251
252 return ti;
253 }
254
255 const std::string& name() const {
256 return name_;
257 }
258
259 const auto& fields() const {
260 return fields_;
261 }
262
263 std::optional<const field_info*> field_by_name(const std::string& name) const {
264 if (fields_by_name_.contains(name)) {
265 return fields_by_name_.at(name);
266 }
267 return std::nullopt;
268 }
269 std::optional<const field_info*> field_by_offset(const std::size_t& offset) const {
270 if (fields_by_offset_.contains(offset)) {
271 return fields_by_offset_.at(offset);
272 }
273 return std::nullopt;
274 }
275
276 std::size_t hash_code() const {
277 return type_id_;
278 }
279 std::size_t id() const {
280 return type_id_;
281 }
282
283 template <typename T>
284 bool is_type() const {
285 static type_id_t tid = type_id<T>;
286 return type_id_ == tid;
287 }
288
289 template <template <typename...> typename Pack>
290 bool is_pack() const {
291 static type_id_t pid = pack_id<Pack>;
292 return pack_id_ == pid;
293 }
294
295 template <template <typename T, std::size_t S> typename Pack>
296 bool is_pack_1t1i() const {
297 static type_id_t pid = pack_1t1i_id<Pack>;
298 return pack_id_ == pid;
299 }
300
301 bool is_const() const {
302 return is_const_;
303 }
304 bool is_indirect() const {
305 // return is_lval_ref() || is_rval_ref() || is_ptr();
306 return indirect_type_id_.has_value();
307 }
308 bool is_rval_ref() const {
309 return is_rval_ref_;
310 }
311 bool is_lval_ref() const {
312 return is_lval_ref_;
313 }
314 bool is_ptr() const {
315 return is_ptr_;
316 }
317
318 const type_info& indirect_type() const {
319 if (indirect_type_id_.has_value()) {
320 return type_registry[indirect_type_id_.value()];
321 } else {
322 return type_registry[0];
323 }
324 }
325
326 std::vector<const type_info*> pack_parameter_types() const {
327 std::vector<const type_info*> tis{};
328 for (const auto& tid: pack_param_ids_) {
329 tis.emplace_back(&type_registry[tid]);
330 }
331 return tis;
332 }
333
334 void* make_copy_of(const void* ptr) const {
335 if (copy_construct_function_.has_value()) {
336 return copy_construct_function_.value()(ptr);
337 }
338 return nullptr;
339 }
340
341 void assign_copy_of(const void* src, void* dest) const {
342 if (copy_assign_function_.has_value()) {
343 copy_assign_function_.value()(dest, src);
344 }
345 }
346
347 bool equality(const void* lhs, const void* rhs) const {
348 if (equality_function_.has_value()) {
349 return equality_function_.value()(lhs, rhs);
350 }
351 return false;
352 }
353
354 private:
355 std::string name_{};
356 std::list<field_info> fields_{};
357 std::unordered_map<std::string, const field_info*> fields_by_name_{};
358 std::unordered_map<std::size_t, const field_info*> fields_by_offset_{};
359 std::list<method_info> methods_{};
360 std::unordered_map<std::string, const method_info*> methods_by_name_{};
361
362 bool is_const_ = false;
363 bool is_lval_ref_ = false;
364 bool is_rval_ref_ = false;
365 bool is_ptr_ = false;
366
367 type_id_t type_id_{};
368 std::optional<type_id_t> indirect_type_id_{std::nullopt};
369 type_id_t pack_id_{};
370 std::vector<type_id_t> pack_param_ids_{};
371
372 [[refl::ignore]]
373 std::optional<std::function<void*(const void*)>> copy_construct_function_{std::nullopt};
374 [[refl::ignore]]
375 std::optional<std::function<void(void*,const void*)>> copy_assign_function_{std::nullopt};
376 [[refl::ignore]]
377 std::optional<std::function<bool(const void*,const void*)>> equality_function_{std::nullopt};
378 };
379
380
381 template <template <typename...> typename Pack, typename... Args>
382 std::vector<type_id_t> get_pack_param_ids<Pack<Args...>>::vector() {
383 std::vector<type_id_t> ids{};
384 (ids.push_back(type_info::from<Args>().id()), ...);
385 return ids;
386 }
387 template <template <typename, std::size_t> typename Pack, typename T, std::size_t I>
388 std::vector<type_id_t> get_pack_param_ids<Pack<T, I>>::vector() {
389 std::vector<type_id_t> ids{};
390 ids.push_back(type_info::from<T>().id());
391 ids.push_back(I);
392 return ids;
393 }
394
395
397 return tif().id();
398 }
399}
400
401export template<>
402struct std::hash<refl::field_path> {
403 std::size_t operator()(const refl::field_path& path) const {
404 std::size_t seed = path.fields_.size();
405 for (const auto &v: path.fields_) {
406 seed ^= std::hash<std::size_t>{}(reinterpret_cast<std::size_t>(v)) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
407 }
408 return seed;
409 }
410};
411
bool is_pack_1t1i() const
bool is_rval_ref() const
std::optional< const field_info * > field_by_name(const std::string &name) const
std::vector< const type_info * > pack_parameter_types() const
bool is_pack() const
bool is_ptr() const
const std::string & name() const
const auto & fields() const
bool equality(const void *lhs, const void *rhs) const
void * make_copy_of(const void *ptr) const
bool is_lval_ref() const
std::size_t hash_code() const
bool is_indirect() const
std::size_t id() const
void assign_copy_of(const void *src, void *dest) const
std::optional< const field_info * > field_by_offset(const std::size_t &offset) const
bool is_type() const
static const type_info & from()
const type_info & indirect_type() const
bool is_const() const
constexpr type_id_t pack_1t1i_id
std::size_t type_id_t
Definition _types.cppm:30
constexpr type_id_t type_id
std::map< type_id_t, type_info > type_registry
bool deep_eq(const R &lhs, const R &rhs)
constexpr std::size_t field_count
constexpr auto type_name
type_id_t get_id_from_info_getter(const type_info &(*tif)())
constexpr type_id_t pack_type_id
constexpr type_id_t pack_id
constexpr std::size_t method_count
const MetadataType & get_metadata() const
std::string name
access_spec access_type
std::size_t offset
void * get_ptr(void *obj) const
std::size_t index
T & get_ref(void *obj) const
type_id_t type_id
bool has_metadata() const
std::vector< std::pair< const type_info &(*)(), void * > > metadata
std::size_t size
void * get_ptr(void *obj) const
T & get_ref(void *obj) const
field_path(const field_info *field)
const type_info & type() const
field_path(std::initializer_list< const field_info * > fields)
bool operator==(const field_path &other) const
static constexpr const char * name
static constexpr std::size_t offset
static std::vector< type_id_t > vector()
static std::vector< type_id_t > vector()
static std::vector< type_id_t > vector()
access_spec access_type
std::size_t index
static constexpr const char * name
std::size_t operator()(const refl::field_path &path) const