Toast Engine
Loading...
Searching...
No Matches
reflect_node.hpp
Go to the documentation of this file.
1
6
7#pragma once
8
9#include <array>
10#include <toast/reflect/reflect.hpp>
11#include <type_traits>
12#include <unordered_map>
13#include <utility>
14
15namespace toast {
16
17class Node;
18struct NodeInfo;
19
20namespace _detail {
21// hi dante
22void TOAST_API callNodeScripts(Node* node, std::string_view name, std::span<const std::any> args) noexcept;
23void TOAST_API setNodeScriptVar(Node* node, std::string_view name, const std::any& value) noexcept;
24auto TOAST_API getNodeScriptVar(const Node* node, std::string_view name) noexcept -> std::any;
25
26template<typename R = void, typename... Args>
27auto callMethodChain(const NodeInfo* info, void* obj, std::string_view method_name, const Args&... args) -> R;
28}
29
36enum class TickFunctionList : uint16_t {
37 none = 0,
38 editor_tick = 1 << 1,
39 init = 1 << 2,
40 destroy = 1 << 3,
41 begin = 1 << 4,
42 end = 1 << 5,
43 on_enable = 1 << 6,
44 on_disable = 1 << 7,
45 early_tick = 1 << 8,
46 tick = 1 << 9,
47 post_physics = 1 << 10,
48 late_tick = 1 << 11,
49 load = 1 << 12,
50 save = 1 << 13,
51 physics_tick = 1 << 14,
52 tick_mask = early_tick | tick | physics_tick | post_physics | late_tick | editor_tick,
53 all = 0xFFFF,
54};
55
56// Bitwise operators for TickFunctionList
57constexpr auto operator&(TickFunctionList lhs, TickFunctionList rhs) -> TickFunctionList {
58 return static_cast<TickFunctionList>(static_cast<uint16_t>(lhs) & static_cast<uint16_t>(rhs));
59}
60
61constexpr auto operator|(TickFunctionList lhs, TickFunctionList rhs) -> TickFunctionList {
62 return static_cast<TickFunctionList>(static_cast<uint16_t>(lhs) | static_cast<uint16_t>(rhs));
63}
64
65constexpr auto operator&=(TickFunctionList& lhs, TickFunctionList rhs) -> TickFunctionList& {
66 lhs = lhs & rhs;
67 return lhs;
68}
69
70constexpr auto operator|=(TickFunctionList& lhs, TickFunctionList rhs) -> TickFunctionList& {
71 lhs = lhs | rhs;
72 return lhs;
73}
74
75// Helper to check if a flag is set
76constexpr auto hasFlag(TickFunctionList flags, TickFunctionList flag) -> bool {
77 return (flags & flag) == flag;
78}
79
80constexpr auto tickFunctionName(TickFunctionList func) -> std::string_view {
81 switch (func) {
82 case TickFunctionList::load: return "load";
83 case TickFunctionList::save: return "save";
84 case TickFunctionList::editor_tick: return "editor_tick";
85 case TickFunctionList::init: return "init";
86 case TickFunctionList::destroy: return "destroy";
87 case TickFunctionList::begin: return "begin";
88 case TickFunctionList::end: return "end";
89 case TickFunctionList::on_enable: return "on_enable";
90 case TickFunctionList::on_disable: return "on_disable";
91 case TickFunctionList::early_tick: return "early_tick";
92 case TickFunctionList::tick: return "tick";
93 case TickFunctionList::physics_tick: return "physics_tick";
94 case TickFunctionList::post_physics: return "post_physics";
95 case TickFunctionList::late_tick: return "late_tick";
96 default: return "none";
97 }
98}
99
111 using Invoker = void (*)(void*);
112
113 TickFunctionList list;
114
115 Invoker load = nullptr;
116 Invoker save = nullptr;
117 Invoker editor_tick = nullptr;
118 Invoker init = nullptr;
119 Invoker destroy = nullptr;
120 Invoker begin = nullptr;
121 Invoker end = nullptr;
122 Invoker on_enable = nullptr;
123 Invoker on_disable = nullptr;
124 Invoker early_tick = nullptr;
125 Invoker tick = nullptr;
126 Invoker physics_tick = nullptr;
127 Invoker post_physics = nullptr;
128 Invoker late_tick = nullptr;
129};
130
137struct TOAST_API SubgroupInfo {
138 std::string_view name;
139 std::span<const FieldInfo* const> fields;
140
146 [[nodiscard]]
147 auto getField(std::string_view field_name) const -> const FieldInfo* {
148 for (const auto& f : fields) {
149 if (field_name == f->name) {
150 return f;
151 }
152 }
153 return nullptr;
154 }
155};
156
163struct TOAST_API GroupInfo {
164 std::string_view name;
165 std::span<const FieldInfo* const> fields;
166 std::span<const SubgroupInfo> subgroups;
167
173 [[nodiscard]]
174 auto getSubgroup(std::string_view group_name) const -> const SubgroupInfo* {
175 for (const auto& g : subgroups) {
176 if (group_name == g.name) {
177 return &g;
178 }
179 }
180 return nullptr;
181 }
182
189 [[nodiscard]]
190 auto getField(std::string_view field_name) const -> const FieldInfo* {
191 for (const auto& f : fields) {
192 if (field_name == f->name) {
193 return f;
194 }
195 }
196 return nullptr;
197 }
198};
199
210struct TOAST_API NodeInfo {
211 using Factory = Node* (*)();
212 using Deleter = void (*)(Node*);
213
214 std::string_view type;
215 const NodeInfo* base_type;
216 std::span<const SignalInfo> signals;
217 std::span<const FieldInfo> all_fields;
218 std::span<const FieldInfo* const> fields;
219 std::span<const GroupInfo> groups;
220 std::span<const FunctionInfo> methods;
221
222 TickFunctions functions;
223
224 Factory construct = nullptr;
225 Deleter destroy = nullptr;
226
232 [[nodiscard]]
233 auto getSignal(std::string_view field_name) const -> const SignalInfo* {
234 for (const auto& f : signals) {
235 if (field_name == f.name) {
236 return &f;
237 }
238 }
239 if (base_type) {
240 return base_type->getSignal(field_name);
241 }
242 return nullptr;
243 }
244
245 [[nodiscard]]
246 auto getSignal(std::string_view declaring_type, std::string_view signal_name) const -> const SignalInfo* {
247 for (const auto* info = this; info != nullptr; info = info->base_type) {
248 if (info->type != declaring_type) {
249 continue;
250 }
251 for (const auto& signal : info->signals) {
252 if (signal.name == signal_name) {
253 return &signal;
254 }
255 }
256 return nullptr;
257 }
258 return nullptr;
259 }
260
261 template<typename F>
262 void forEachSignal(F&& callback) const {
263 for (const auto* info = this; info != nullptr; info = info->base_type) {
264 for (const auto& signal : info->signals) {
265 callback(*info, signal);
266 }
267 }
268 }
269
275 [[nodiscard]]
276 auto getField(std::string_view field_name) const -> const FieldInfo* {
277 for (const auto& f : all_fields) {
278 if (field_name == f.name) {
279 return &f;
280 }
281 }
282 if (base_type) {
283 return base_type->getField(field_name);
284 }
285 return nullptr;
286 }
287
293 [[nodiscard]]
294 auto getMethod(std::string_view method_name) const -> const FunctionInfo* {
295 for (const auto& m : methods) {
296 if (method_name == m.name) {
297 return &m;
298 }
299 }
300 if (base_type) {
301 return base_type->getMethod(method_name);
302 }
303 return nullptr;
304 }
305
311 template<typename R = void, typename... Args>
312 auto call(void* obj, std::string_view method_name, Args&&... args) const -> R {
313 if (!getMethod(method_name)) {
314 TOAST_WARN("Reflect", "call(): reflected function '{}' not found on '{}'", method_name, type);
315 assert(false && "NodeInfo::call(): reflected function not found");
316 if constexpr (std::is_void_v<R>) {
317 return;
318 } else {
319 return R {};
320 }
321 }
322 // Build an any-args array so args are forwarded to Lua scripts as well
323 std::array<std::any, sizeof...(Args)> any_args {std::any(std::decay_t<Args>(args))...};
324 if constexpr (std::is_void_v<R>) {
325 _detail::callMethodChain(this, obj, method_name, std::forward<Args>(args)...);
326 _detail::callNodeScripts(static_cast<Node*>(obj), method_name, any_args);
327 } else {
328 R result = _detail::callMethodChain<R>(this, obj, method_name, std::forward<Args>(args)...);
329 _detail::callNodeScripts(static_cast<Node*>(obj), method_name, any_args);
330 return result;
331 }
332 }
333
338 [[nodiscard]]
339 auto callAllDynamic(void* obj, std::string_view method_name, std::span<const std::any> args) const -> std::any {
340 std::any result;
341 if (base_type) {
342 result = base_type->callAllDynamic(obj, method_name, args);
343 }
344 for (const auto& m : methods) {
345 if (m.name == method_name) {
346 result = m.callDynamic(obj, args);
347 break;
348 }
349 }
350 return result;
351 }
352
358 [[nodiscard]]
359 auto isA(const NodeInfo* other) const -> bool {
360 for (const NodeInfo* cur = this; cur; cur = cur->base_type) {
361 if (cur == other) {
362 return true;
363 }
364 }
365 return false;
366 }
367
373 [[nodiscard]]
374 auto hasFunction(TickFunctionList mask) const -> bool {
375 for (const NodeInfo* cur = this; cur; cur = cur->base_type) {
376 if ((cur->functions.list & mask) != TickFunctionList::none) {
377 return true;
378 }
379 }
380 return false;
381 }
382
384 [[nodiscard]]
385 constexpr auto id() const -> uint32_t {
386 uint32_t hash = 2166136261u;
387 for (char c : type) {
388 hash ^= static_cast<uint8_t>(c);
389 hash *= 16777619u;
390 }
391 return hash;
392 }
393
399 [[nodiscard]]
400 auto getGroup(std::string_view group_name) const -> const GroupInfo* {
401 for (const auto& g : groups) {
402 if (group_name == g.name) {
403 return &g;
404 }
405 }
406 if (base_type) {
407 return base_type->getGroup(group_name);
408 }
409 return nullptr;
410 }
411
418 [[nodiscard]]
419 auto search(std::string_view field_name) const -> const FieldInfo* {
420 for (const auto& f : all_fields) {
421 if (field_name == f.name) {
422 return &f;
423 }
424 }
425 if (base_type) {
426 return base_type->search(field_name);
427 }
428 return nullptr;
429 }
430
436 template<typename F>
437 void forEachBaseType(F&& fn) const {
438 if (base_type) {
439 base_type->forEachBaseType(fn);
440 }
441 fn(*this);
442 }
443};
444
445template<class T>
447 [[nodiscard]]
448 static auto get() noexcept -> const NodeInfo*;
449};
450
451namespace _detail {
452
453// Walks base->derived, calling the method at every level where it is defined
454template<typename R, typename... Args>
455auto callMethodChain(const NodeInfo* info, void* obj, std::string_view method_name, const Args&... args) -> R {
456 if constexpr (std::is_void_v<R>) {
457 if (info->base_type) {
458 callMethodChain(info->base_type, obj, method_name, args...);
459 }
460 for (const auto& m : info->methods) {
461 if (m.name == method_name) {
462 m.call(obj, args...);
463 break;
464 }
465 }
466 } else {
467 R result {};
468 if (info->base_type) {
469 result = callMethodChain<R>(info->base_type, obj, method_name, args...);
470 }
471 for (const auto& m : info->methods) {
472 if (m.name == method_name) {
473 result = m.call<R>(obj, args...);
474 break;
475 }
476 }
477 return result;
478 }
479}
480
481}
482
492class TOAST_API NodeRegistry {
493public:
494 NodeRegistry();
495
497 static void registerNode(const NodeInfo* info);
498
504 [[nodiscard]]
505 static auto reflect(std::string_view name) -> const NodeInfo* {
506 // remove empty namespace indicator if in global space
507 if (name.starts_with("::")) {
508 name.remove_prefix(2);
509 }
510 auto types = (*instance).types;
511 auto it = (*instance).types.find(name);
512 return it != (*instance).types.end() ? it->second : nullptr;
513 }
514
516 template<typename F>
517 static void forEachType(F&& fn) {
518 if (instance) {
519 for (const auto& [key, info] : instance->types) {
520 fn(info);
521 }
522 }
523 }
524
525private:
526 std::unordered_map<std::string_view, const NodeInfo*> types;
527 static NodeRegistry* instance;
528};
529
530TOAST_API void registerEngineTypes();
531
532template<class T>
533auto nodeTypeInfo() noexcept -> const NodeInfo*;
534
535}
Global registry mapping fully-qualified type names to NodeInfo pointers.
Definition reflect_node.hpp:492
static void forEachType(F &&fn)
Calls fn(info) for every registered node type.
Definition reflect_node.hpp:517
static void registerNode(const NodeInfo *info)
Called by the generated registration function; inserts a type into the registry.
Definition engine.cpp:111
static auto reflect(std::string_view name) -> const NodeInfo *
Looks up a Node type by its fully-qualified C++ name.
Definition reflect_node.hpp:505
Definition node.hpp:81
#define TOAST_WARN(sink,...)
Indicates possible code missuse.
Definition log.hpp:94
Member-robber pattern used by the generated reflection code.
Definition defer.hpp:16
TickFunctionList
Bit-flag set describing which lifecycle phases a Node type implements.
Definition reflect_node.hpp:36
Runtime descriptor for one reflected field.
Definition reflect.hpp:79
Runtime descriptor for one reflected member function.
Definition reflect.hpp:157
Top-level category grouping fields and optional SubgroupInfo children.
Definition reflect_node.hpp:163
auto getField(std::string_view field_name) const -> const FieldInfo *
Finds a field by name within this group's flat field list.
Definition reflect_node.hpp:190
auto getSubgroup(std::string_view group_name) const -> const SubgroupInfo *
Finds a subgroup by name.
Definition reflect_node.hpp:174
Complete reflection record for a registered Node type.
Definition reflect_node.hpp:210
auto getMethod(std::string_view method_name) const -> const FunctionInfo *
Finds a reflected function by name in this type, then walks base_type if not found.
Definition reflect_node.hpp:294
auto getGroup(std::string_view group_name) const -> const GroupInfo *
Finds a group by name, walking base_type if not found.
Definition reflect_node.hpp:400
auto getField(std::string_view field_name) const -> const FieldInfo *
Finds a field by name in this type's all_fields, then walks base_type if not found.
Definition reflect_node.hpp:276
auto call(void *obj, std::string_view method_name, Args &&... args) const -> R
Walks base->derived calling every inheritance level that defines method_name, then fires Lua scripts.
Definition reflect_node.hpp:312
void forEachBaseType(F &&fn) const
Calls fn for each NodeInfo in the inheritance chain, from the root base down to this type.
Definition reflect_node.hpp:437
auto callAllDynamic(void *obj, std::string_view method_name, std::span< const std::any > args) const -> std::any
Walks base->derived via dynamic invokers, calling every level that defines method_name.
Definition reflect_node.hpp:339
constexpr auto id() const -> uint32_t
FNV-1a hash of the type string; not the same as a Node's runtime UID.
Definition reflect_node.hpp:385
auto hasFunction(TickFunctionList mask) const -> bool
Checks whether this type or any base type implements the given lifecycle phase(s).
Definition reflect_node.hpp:374
auto search(std::string_view field_name) const -> const FieldInfo *
Finds a field by name, walking the full inheritance chain.
Definition reflect_node.hpp:419
auto isA(const NodeInfo *other) const -> bool
Checks whether this type is the same as or a subtype of another.
Definition reflect_node.hpp:359
auto getSignal(std::string_view field_name) const -> const SignalInfo *
Finds a signal by name in this type's signals, then walks base_type if not found.
Definition reflect_node.hpp:233
Definition reflect_node.hpp:446
Definition reflect.hpp:52
A named sub-section of fields inside a GroupInfo.
Definition reflect_node.hpp:137
auto getField(std::string_view field_name) const -> const FieldInfo *
Linear scan for a field by name within this subgroup.
Definition reflect_node.hpp:147
Set of lifecycle function pointers for one Node type.
Definition reflect_node.hpp:109
void(*)(void *) Invoker
void* erases the Node* to avoid a vtable; generated code casts it back before calling
Definition reflect_node.hpp:111