29#ifndef ULTRALIGHT_REFLECTION
30#if defined(__clang__) || defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1940)
31#define ULTRALIGHT_REFLECTION 1
33#define ULTRALIGHT_REFLECTION 0
39#if ULTRALIGHT_REFLECTION
45constexpr std::string_view RawValueSig() {
46#if defined(__clang__) || defined(__GNUC__)
47 return __PRETTY_FUNCTION__;
54constexpr std::string_view RawTypeSig() {
55#if defined(__clang__) || defined(__GNUC__)
56 return __PRETTY_FUNCTION__;
62constexpr bool IsIdentChar(
char c) {
63 return (c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9')
72constexpr std::string_view ValueRegion(std::string_view sig) {
73#if defined(__clang__) || defined(__GNUC__)
74 size_t open = sig.find(
'[');
75 if (open == std::string_view::npos)
77 size_t eq = sig.find(
"= ", open);
78 if (eq == std::string_view::npos)
80 size_t end = sig.rfind(
']');
81 if (end == std::string_view::npos || end <= eq + 2)
83 size_t next = sig.find(
"; ", eq);
84 if (next != std::string_view::npos && next < end)
86 return sig.substr(eq + 2, end - (eq + 2));
88 size_t open = sig.find(
'<');
89 if (open == std::string_view::npos)
91 size_t end = sig.rfind(
">(void)");
92 if (end == std::string_view::npos || end <= open + 1)
94 return sig.substr(open + 1, end - (open + 1));
102constexpr std::string_view ParseEnumeratorName(std::string_view sig) {
103 std::string_view region = ValueRegion(sig);
106 size_t start = region.size();
107 while (start > 0 && IsIdentChar(region[start - 1]))
109 if (start == region.size())
111 std::string_view name = region.substr(start);
112 if (name[0] >=
'0' && name[0] <=
'9')
120constexpr std::string_view ParseFieldName(std::string_view sig) {
121 size_t anchor = std::string_view::npos;
122 for (
size_t i = 0; i + 1 < sig.size(); i++) {
123 if (sig[i] ==
'.' && IsIdentChar(sig[i + 1]))
125 if (sig[i] ==
'-' && sig[i + 1] ==
'>' && i + 2 < sig.size() && IsIdentChar(sig[i + 2]))
128 if (anchor == std::string_view::npos)
131 while (end < sig.size() && IsIdentChar(sig[end]))
133 while (end + 2 < sig.size() && sig[end] ==
':' && sig[end + 1] ==
':' && IsIdentChar(sig[end + 2])) {
136 while (end < sig.size() && IsIdentChar(sig[end]))
139 return sig.substr(anchor, end - anchor);
144constexpr std::string_view ParseTypeName(std::string_view sig) {
145 std::string_view region = ValueRegion(sig);
148 size_t end = region.size();
149 while (end > 0 && region[end - 1] ==
' ')
151 if (end > 0 && region[end - 1] ==
'>') {
155 if (region[end] ==
'>')
157 else if (region[end] ==
'<')
159 }
while (end > 0 && depth > 0);
162 while (start > 0 && IsIdentChar(region[start - 1]))
164 return region.substr(start, end - start);
170 char data[N + 1] = {};
172 constexpr void Append(std::string_view s) {
181constexpr bool InUnderlyingRange(
long long v) {
182 if constexpr (std::is_signed_v<U>) {
183 return v >=
static_cast<long long>((std::numeric_limits<U>::min)())
184 && v <=
static_cast<long long>((std::numeric_limits<U>::max)());
187 &&
static_cast<unsigned long long>(v)
188 <=
static_cast<unsigned long long>((std::numeric_limits<U>::max)());
198template <
typename E,
long long I>
199constexpr std::string_view EnumeratorName() {
200 using U = std::underlying_type_t<E>;
201 if constexpr (!InUnderlyingRange<U>(I)) {
204 return ParseEnumeratorName(RawValueSig<std::bit_cast<E>(
static_cast<U
>(I))>());
210enum class ReflectionProbe { kProbeValue = 1 };
211static_assert(EnumeratorName<ReflectionProbe, 1>() ==
"kProbeValue",
212 "enum name reflection failed on this compiler; define "
213 "ULTRALIGHT_REFLECTION=0 and specialize the family's TypeTraits explicitly");
214static_assert(EnumeratorName<ReflectionProbe, 2>().empty(),
215 "enum name reflection failed to reject a non-enumerator value");
216enum ReflectionUnscopedProbe { kUnscopedProbeA, kUnscopedProbeB };
217static_assert(EnumeratorName<ReflectionUnscopedProbe, 1>() ==
"kUnscopedProbeB",
218 "unscoped-enum name reflection failed on this compiler; define "
219 "ULTRALIGHT_REFLECTION=0 and specialize the family's TypeTraits explicitly");
220static_assert(EnumeratorName<ReflectionUnscopedProbe, 63>().empty(),
221 "enum name reflection failed to skip a value beyond an unscoped enum's "
222 "representable range");
227 template <
typename T>
228 constexpr operator T()
const;
231template <
typename T,
size_t... Is>
232constexpr bool BraceConstructible(std::index_sequence<Is...>) {
233 return requires { T { ((void)Is, AnyType {})... }; };
236template <
typename T,
size_t N = 0>
237constexpr size_t CountFields() {
238 if constexpr (N > 24) {
239 static_assert(N <= 24,
"aggregate reflection supports at most 24 fields; specialize "
240 "the family's TypeTraits for larger types");
242 }
else if constexpr (BraceConstructible<T>(std::make_index_sequence<N + 1> {})) {
243 return CountFields<T, N + 1>();
249template <
size_t I,
typename... Ms>
250constexpr auto NthAddress(Ms&... members)
noexcept {
251 return &std::get<I>(std::tie(members...));
254template <
size_t I,
typename T>
255constexpr auto MemberPointer(T& t)
noexcept {
256 constexpr size_t kCount = CountFields<std::remove_cvref_t<T>>();
257 static_assert(I < kCount);
258 if constexpr (kCount == 1) {
260 return NthAddress<I>(m0);
261 }
else if constexpr (kCount == 2) {
263 return NthAddress<I>(m0, m1);
264 }
else if constexpr (kCount == 3) {
265 auto& [m0, m1, m2] = t;
266 return NthAddress<I>(m0, m1, m2);
267 }
else if constexpr (kCount == 4) {
268 auto& [m0, m1, m2, m3] = t;
269 return NthAddress<I>(m0, m1, m2, m3);
270 }
else if constexpr (kCount == 5) {
271 auto& [m0, m1, m2, m3, m4] = t;
272 return NthAddress<I>(m0, m1, m2, m3, m4);
273 }
else if constexpr (kCount == 6) {
274 auto& [m0, m1, m2, m3, m4, m5] = t;
275 return NthAddress<I>(m0, m1, m2, m3, m4, m5);
276 }
else if constexpr (kCount == 7) {
277 auto& [m0, m1, m2, m3, m4, m5, m6] = t;
278 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6);
279 }
else if constexpr (kCount == 8) {
280 auto& [m0, m1, m2, m3, m4, m5, m6, m7] = t;
281 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7);
282 }
else if constexpr (kCount == 9) {
283 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8] = t;
284 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8);
285 }
else if constexpr (kCount == 10) {
286 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9] = t;
287 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9);
288 }
else if constexpr (kCount == 11) {
289 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10] = t;
290 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10);
291 }
else if constexpr (kCount == 12) {
292 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11] = t;
293 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11);
294 }
else if constexpr (kCount == 13) {
295 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12] = t;
296 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12);
297 }
else if constexpr (kCount == 14) {
298 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13] = t;
299 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13);
300 }
else if constexpr (kCount == 15) {
301 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14] = t;
302 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14);
303 }
else if constexpr (kCount == 16) {
304 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15] = t;
305 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
307 }
else if constexpr (kCount == 17) {
308 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16] = t;
309 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
311 }
else if constexpr (kCount == 18) {
312 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17]
314 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
316 }
else if constexpr (kCount == 19) {
317 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17,
320 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
322 }
else if constexpr (kCount == 20) {
323 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17,
326 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
327 m15, m16, m17, m18, m19);
328 }
else if constexpr (kCount == 21) {
329 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17,
332 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
333 m15, m16, m17, m18, m19, m20);
334 }
else if constexpr (kCount == 22) {
335 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17,
338 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
339 m15, m16, m17, m18, m19, m20, m21);
340 }
else if constexpr (kCount == 23) {
341 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17,
342 m18, m19, m20, m21, m22]
344 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
345 m15, m16, m17, m18, m19, m20, m21, m22);
347 auto& [m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17,
348 m18, m19, m20, m21, m22, m23]
350 return NthAddress<I>(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
351 m15, m16, m17, m18, m19, m20, m21, m22, m23);
357#if defined(__clang__)
358#pragma clang diagnostic push
359#pragma clang diagnostic ignored "-Wundefined-var-template"
375FieldAnchor(P) -> FieldAnchor<P>;
377template <
typename T,
size_t I>
378constexpr std::string_view FieldNameOf() {
379 return ParseFieldName(RawValueSig<FieldAnchor { MemberPointer<I>(fake_obj<T>) }>());
382#if defined(__clang__)
383#pragma clang diagnostic pop
387constexpr std::string_view UnqualifiedTypeName() {
388 return ParseTypeName(RawTypeSig<T>());
392struct ReflectionFieldProbe {
395static_assert(FieldNameOf<ReflectionFieldProbe, 0>() ==
"probe_field",
396 "field name reflection failed on this compiler; define "
397 "ULTRALIGHT_REFLECTION=0 and specialize the family's TypeTraits explicitly");
398static_assert(UnqualifiedTypeName<ReflectionFieldProbe>() ==
"ReflectionFieldProbe",
399 "type name reflection failed on this compiler; define "
400 "ULTRALIGHT_REFLECTION=0 and specialize the family's TypeTraits explicitly");
402struct ReflectionTemplateProbe {
405static_assert(UnqualifiedTypeName<ReflectionTemplateProbe<int>>() ==
"ReflectionTemplateProbe",
406 "type name reflection failed on a class-template instantiation; define "
407 "ULTRALIGHT_REFLECTION=0 and specialize the family's TypeTraits explicitly");
411struct ReflectionFieldCollapseProbe {
412 int collapse_probe_field;
414static_assert(FieldNameOf<ReflectionFieldCollapseProbe, 0>() ==
"collapse_probe_field",
415 "field name reflection collapsed across types on this compiler; define "
416 "ULTRALIGHT_REFLECTION=0 and specialize the family's TypeTraits explicitly");
418template <
typename T,
size_t I>
419inline constexpr auto kFieldNameZ = [] {
420 constexpr std::string_view name = FieldNameOf<T, I>();
421 ZString<name.size()> s {};
427inline constexpr auto kTypeNameZ = [] {
428 constexpr std::string_view name = UnqualifiedTypeName<T>();
429 ZString<name.size()> s {};
434template <
typename T,
size_t I>
436 = std::remove_pointer_t<decltype(MemberPointer<I>(std::declval<T&>()))>;
439inline constexpr auto kFieldNames = []<
size_t... Is>(std::index_sequence<Is...>) {
440 return std::array<
const char*,
sizeof...(Is)> { kFieldNameZ<T, Is>.data... };
441}(std::make_index_sequence<CountFields<T>()> {});
Root namespace for every public Ultralight type, function, and enumeration.