Toast Engine
Loading...
Searching...
No Matches
reflect.hpp
1
8
9#pragma once
10
11#include "toast/uid.hpp"
12
13#include <any>
14#include <cassert>
15#include <cstdint>
16#include <nlohmann/json.hpp>
17#include <optional>
18#include <span>
19#include <string>
20#include <string_view>
21#include <toast/events/signal_types.hpp>
22#include <toast/export.hpp>
23#include <toast/log.hpp>
24#include <toast/world/box.hpp>
25#include <type_traits>
26#include <typeinfo>
27#include <utility>
28
29namespace toast {
30
31class Node;
32
39enum class FieldType : uint8_t {
40 bool_t,
41 int_t,
42 float_t,
43 string_t,
44 double_t,
45 uid_t,
46 vec2_t,
47 vec3_t,
48 vec4_t,
49 quaternion_t,
50};
51
52struct TOAST_API SignalInfo {
53 using SignalGetterPtr = std::vector<signals::ConnectionInfo> (*)(void*);
54 using SignalConnectPtr = void (*)(void*, Node&, std::string_view, signals::ConnectionSource, bool);
55 using SignalDisconnectPtr = void (*)(void*, Node&, std::string_view, signals::ConnectionSource);
56 using SignalClearPtr = void (*)(void*, signals::ConnectionSource);
57 using SignalFirePtr = bool (*)(void*, std::span<const std::any>);
58
59 std::string_view name;
60 std::string_view type;
61 std::vector<const std::type_info*> args;
62 nlohmann::json attributes;
63
64 SignalGetterPtr get;
65 SignalConnectPtr connect;
66 SignalDisconnectPtr disconnect;
67 SignalClearPtr clear;
68 SignalFirePtr fire;
69};
70
79struct TOAST_API FieldInfo {
80 using FieldGetterPtr = std::any (*)(void*);
81 using FieldSetterPtr = void (*)(void*, std::any);
82
83 std::string_view name;
84 std::string_view type; // C++ type name
85 FieldType value_type; // serialization kind
86 nlohmann::json attributes;
87 bool is_array = false; // True if field is vector
88
89 FieldGetterPtr get;
90 FieldSetterPtr set;
91
95 [[nodiscard]]
96 auto attributeMap() const -> const nlohmann::json* {
97 if (attributes.is_object()) {
98 return &attributes;
99 }
100 if (attributes.is_array() && attributes.size() == 1 && attributes.front().is_object()) {
101 return &attributes.front();
102 }
103 return nullptr;
104 }
105
107 [[nodiscard]]
108 auto groupName() const -> std::string {
109 return getAttribute("Group");
110 }
111
117 [[nodiscard]]
118 auto hasAttribute(std::string_view attr_name) const -> bool {
119 const auto* map = attributeMap();
120 return map != nullptr && map->contains(std::string(attr_name));
121 }
122
128 [[nodiscard]]
129 auto getAttribute(std::string_view attr_name) const -> std::string {
130 const auto* map = attributeMap();
131 if (map == nullptr) {
132 return "";
133 }
134 auto it = map->find(std::string(attr_name));
135 if (it == map->end() || !it->is_array() || it->empty()) {
136 return "";
137 }
138 return it->at(0).get<std::string>();
139 }
140};
141
145struct TOAST_API ParameterInfo {
146 std::string_view name;
147 std::string_view type; // C++ type name, as spelled in the header
148 std::optional<std::string_view> default_value;
149 const std::type_info* type_id; // typeid(std::decay_t<param>), for call-time validation
150};
151
157struct TOAST_API FunctionInfo {
159 using Invoker = void (*)();
160
162 using DynamicInvoker = std::any (*)(void* obj, std::span<const std::any> args);
163
164 std::string_view name;
165 std::string_view return_type; // C++ return type name, as spelled in the header
166 const std::type_info* return_type_id; // typeid(std::decay_t<return>), for call-time validation
167 std::span<const ParameterInfo> parameters;
168 nlohmann::json attributes;
169 Invoker invoke = nullptr;
170 DynamicInvoker invoke_dynamic = nullptr;
171
172 [[nodiscard]]
173 auto attributeMap() const -> const nlohmann::json* {
174 if (attributes.is_object()) {
175 return &attributes;
176 }
177 if (attributes.is_array() && attributes.size() == 1 && attributes.front().is_object()) {
178 return &attributes.front();
179 }
180 return nullptr;
181 }
182
183 [[nodiscard]]
184 auto hasAttribute(std::string_view attr_name) const -> bool {
185 const auto* map = attributeMap();
186 return map != nullptr && map->contains(std::string(attr_name));
187 }
188
189 [[nodiscard]]
190 auto getAttribute(std::string_view attr_name) const -> std::string {
191 const auto* map = attributeMap();
192 if (map == nullptr) {
193 return "";
194 }
195 auto it = map->find(std::string(attr_name));
196 if (it == map->end() || !it->is_array() || it->empty()) {
197 return "";
198 }
199 return it->at(0).get<std::string>();
200 }
201
202 template<typename R = void, typename... Args>
203 auto call(void* obj, Args&&... args) const -> R {
204 bool signature_ok =
205 parameters.size() == sizeof...(Args) && return_type_id != nullptr && *return_type_id == typeid(std::decay_t<R>);
206 if (signature_ok) {
207 const std::array<const std::type_info*, sizeof...(Args)> expected = {&typeid(std::decay_t<Args>)...};
208 for (std::size_t i = 0; i < expected.size(); ++i) {
209 if (parameters[i].type_id == nullptr || *parameters[i].type_id != *expected[i]) {
210 signature_ok = false;
211 break;
212 }
213 }
214 }
215 if (!signature_ok) {
216 TOAST_WARN("Reflect", "call(): signature mismatch for '{}'; invoking is undefined behavior", name);
217 assert(false && "FunctionInfo::call(): argument or return type mismatch");
218 }
219
220 using Trampoline = R (*)(void*, std::decay_t<Args>...);
221 auto* trampoline = reinterpret_cast<Trampoline>(invoke);
222 return trampoline(obj, std::forward<Args>(args)...);
223 }
224
233 [[nodiscard]]
234 auto callDynamic(void* obj, std::span<const std::any> args) const -> std::any {
235 if (!invoke_dynamic) {
236 TOAST_WARN("Reflect", "callDynamic(): no dynamic invoker for '{}' (was it compiled with an older generator?)", name);
237 return {};
238 }
239 return invoke_dynamic(obj, args);
240 }
241};
242
243template<class T>
245
255namespace _detail {
256template<typename Tag>
257struct Accessor {
258 inline static typename Tag::type member;
259};
260
261template<typename Tag, typename Tag::type Ptr>
262struct Robber {
263 Robber() { Accessor<Tag>::member = Ptr; }
264
265 static Robber instance;
266};
267
268template<typename Tag, typename Tag::type Ptr>
269Robber<Tag, Ptr> Robber<Tag, Ptr>::instance;
270}
271
283template<class Class, typename FieldType, typename Tag>
285 static auto get(void* obj) -> std::any { return static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member; }
286
287 static void set(void* obj, std::any value) {
288 if (auto* typed = std::any_cast<FieldType>(&value)) {
289 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member = *typed;
290 return;
291 }
292
293 // HACK: Try integer type conversion if exact match fails
294 if constexpr (std::is_integral_v<FieldType>) {
295 const auto& type_info = value.type();
296 if (type_info == typeid(int)) {
297 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member = static_cast<FieldType>(std::any_cast<int>(value));
298 } else if (type_info == typeid(unsigned int)) {
299 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member =
300 static_cast<FieldType>(std::any_cast<unsigned int>(value));
301 } else if (type_info == typeid(long)) {
302 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member = static_cast<FieldType>(std::any_cast<long>(value));
303 } else if (type_info == typeid(unsigned long)) {
304 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member =
305 static_cast<FieldType>(std::any_cast<unsigned long>(value));
306 } else if (type_info == typeid(short)) {
307 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member = static_cast<FieldType>(std::any_cast<short>(value));
308 } else if (type_info == typeid(unsigned short)) {
309 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member =
310 static_cast<FieldType>(std::any_cast<unsigned short>(value));
311 } else if (type_info == typeid(char)) {
312 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member = static_cast<FieldType>(std::any_cast<char>(value));
313 } else if (type_info == typeid(unsigned char)) {
314 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member =
315 static_cast<FieldType>(std::any_cast<unsigned char>(value));
316 }
317 }
318 }
319};
320
324template<class Class, typename FieldType, typename Tag>
326 static auto get(void* obj) -> std::any {
327 if constexpr (std::is_enum_v<FieldType>) {
328 using Underlying = std::underlying_type_t<FieldType>;
329 const Underlying value = static_cast<Underlying>(static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member);
330 if constexpr (std::is_signed_v<Underlying>) {
331 return std::any {static_cast<int64_t>(value)};
332 } else {
333 return std::any {static_cast<uint64_t>(value)};
334 }
335 } else {
336 return FieldAccess<Class, FieldType, Tag>::get(obj);
337 }
338 }
339
340 static void set(void* obj, std::any value) {
341 if constexpr (std::is_enum_v<FieldType>) {
342 auto assign = [obj](auto numeric) {
343 using Underlying = std::underlying_type_t<FieldType>;
344 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member =
345 static_cast<FieldType>(static_cast<Underlying>(numeric));
346 };
347
348 if (const auto* v = std::any_cast<int>(&value)) {
349 assign(*v);
350 } else if (const auto* v = std::any_cast<unsigned int>(&value)) {
351 assign(*v);
352 } else if (const auto* v = std::any_cast<int64_t>(&value)) {
353 assign(*v);
354 } else if (const auto* v = std::any_cast<uint64_t>(&value)) {
355 assign(*v);
356 } else if (const auto* v = std::any_cast<long>(&value)) {
357 assign(*v);
358 } else if (const auto* v = std::any_cast<unsigned long>(&value)) {
359 assign(*v);
360 } else if (const auto* v = std::any_cast<short>(&value)) {
361 assign(*v);
362 } else if (const auto* v = std::any_cast<unsigned short>(&value)) {
363 assign(*v);
364 } else if (const auto* v = std::any_cast<char>(&value)) {
365 assign(*v);
366 } else if (const auto* v = std::any_cast<unsigned char>(&value)) {
367 assign(*v);
368 }
369 } else {
370 FieldAccess<Class, FieldType, Tag>::set(obj, std::move(value));
371 }
372 }
373};
374
381template<class Class, typename T, typename Tag>
382struct FieldAccess<Class, Box<T>, Tag> {
383 static auto get(void* obj) -> std::any {
384 Box<T>& src = static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member;
385 return std::any {Box<Node>(src)};
386 }
387
388 static void set(void* obj, std::any value) {
389 if (auto* box = std::any_cast<Box<Node>>(&value)) {
390 static_cast<Class*>(obj)->*_detail::template Accessor<Tag>::member = box->as<T>();
391 }
392 }
393};
394
395}
pseudo shared pointer for Nodes
Intrusive reference-counted handle to a Node.
Definition box.hpp:35
Definition node.hpp:81
Definition reflect.hpp:244
Public logging interface for the engine.
#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
Field accessor that exchanges enum values through their int representation.
Definition reflect.hpp:325
Generated getter/setter pair binding a private member pointer to the FieldInfo interface.
Definition reflect.hpp:284
Runtime descriptor for one reflected field.
Definition reflect.hpp:79
auto getAttribute(std::string_view attr_name) const -> std::string
Returns the first string value of a JSON annotation.
Definition reflect.hpp:129
auto groupName() const -> std::string
Returns the value of the "Group" annotation, or an empty string if not present.
Definition reflect.hpp:108
auto hasAttribute(std::string_view attr_name) const -> bool
Checks whether a JSON annotation key is present on this field.
Definition reflect.hpp:118
auto attributeMap() const -> const nlohmann::json *
Returns the generated attribute objectpr.
Definition reflect.hpp:96
Runtime descriptor for one reflected member function.
Definition reflect.hpp:157
std::any(*)(void *obj, std::span< const std::any > args) DynamicInvoker
receives args as std::any, returns std::any (void → empty std::any)
Definition reflect.hpp:162
auto callDynamic(void *obj, std::span< const std::any > args) const -> std::any
Calls the function with runtime-typed arguments for scripting dispatch.
Definition reflect.hpp:234
void(*)() Invoker
Generic erased trampoline pointer; reinterpret_cast back to the real signature before calling.
Definition reflect.hpp:159
Runtime descriptor for one parameter of a reflected function.
Definition reflect.hpp:145
Definition reflect.hpp:52
Definition reflect.hpp:257
Definition reflect.hpp:262
Unique identifier for everything that exists in our engine.