microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
reflect_annotate.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
7// std-interop-file: dev-time code-generation tool (std::string-heavy), never part of the formatting hot path.
8
34#include "../detail/compat.hpp"
35
36#include <type_traits>
37
38namespace microfmt {
39
51template <typename T> struct enable_reflect_format : std::false_type {};
52
53} // namespace microfmt
54
55#if RELOCO_HAS_REFLECTION
56
57#include <meta>
58#include <string>
59#include <string_view>
60#include <vector>
61
62namespace microfmt::detail {
63
67struct reflect_dump_entry {
68 std::string type_name;
69 std::string generated_code;
70};
71
77inline std::vector<reflect_dump_entry> &reflect_dump_registry() {
78 static std::vector<reflect_dump_entry> registry;
79 return registry;
80}
81
87template <typename E> std::string generate_reflect_enum_dump(std::string_view type_name) {
88 std::string out;
89 out += "template <> struct microfmt::formatter<";
90 out += type_name;
91 out += "> {\n constexpr void parse(format_parse_context &ctx) noexcept { (void)ctx; }\n";
92 out += " void format(";
93 out += type_name;
94 out += " val, const sink &out) const noexcept {\n switch (val) {\n";
95
96 template for (constexpr auto e : define_static_array(std::meta::enumerators_of(^^E))) {
97 constexpr std::string_view enumerator_name = std::meta::identifier_of(e);
98 out += " case ";
99 out += type_name;
100 out += "::";
101 out += enumerator_name;
102 out += ": out.write(\"";
103 out += enumerator_name;
104 out += "\"); return;\n";
105 }
106
107 out += " default: break;\n }\n format_to(out, \"{}\", static_cast<std::underlying_type_t<";
108 out += type_name;
109 out += ">>(val));\n }\n};\n";
110 return out;
111}
112
133template <typename T> std::string generate_reflect_struct_dump(std::string_view type_name) {
134 std::string members_code;
135 bool has_members = false;
136
137 template for (constexpr auto member :
138 define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::current()))) {
139 constexpr std::string_view name = std::meta::identifier_of(member);
140 if (has_members) {
141 members_code += " out.write(\", \");\n";
142 }
143 has_members = true;
144 members_code += " out.write(\"";
145 members_code += name;
146 members_code += ": \");\n microfmt::format_to(out, \"{}\", microfmt::as_debug(val.";
147 members_code += name;
148 members_code += "));\n";
149 }
150
151 std::string out;
152 out += "template <> struct reloco::Debug<";
153 out += type_name;
154 out += "> {\n static void format(const ";
155 out += type_name;
156 out += " &val, const reloco::sink &out) noexcept {\n";
157
158 if (!has_members) {
159 out += " out.write(\"";
160 out += type_name;
161 out += " {}\");\n";
162 } else {
163 out += " out.write(\"";
164 out += type_name;
165 out += " { \");\n";
166 out += members_code;
167 out += " out.write(\" }\");\n";
168 }
169
170 out += " }\n};\n";
171 return out;
172}
173
174template <typename E> bool register_reflect_dump_enum(std::string_view type_name) {
175 reflect_dump_registry().push_back({std::string(type_name), generate_reflect_enum_dump<E>(type_name)});
176 return true;
177}
178
179template <typename T> bool register_reflect_dump_struct(std::string_view type_name) {
180 reflect_dump_registry().push_back({std::string(type_name), generate_reflect_struct_dump<T>(type_name)});
181 return true;
182}
183
195inline std::string render_reflect_dump() {
196 std::string out = "// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>\n"
197 "//\n// SPDX-License-Identifier: BSD-2-Clause\n\n"
198 "// Auto-generated by tools/reflect_dump -- do not edit by hand.\n"
199 "// Regenerate with a -freflection build; see docs/reflection.md.\n\n"
200 "#pragma once\n\n"
201 "#include <microfmt/detail/compat.hpp>\n\n"
202 "#if !RELOCO_HAS_REFLECTION\n\n"
203 "#include <microfmt/microfmt.hpp>\n"
204 "#include <type_traits>\n\n";
205
206 for (const auto &entry : reflect_dump_registry()) {
207 out += entry.generated_code;
208 out += "\n";
209 }
210
211 out += "#endif // !RELOCO_HAS_REFLECTION\n";
212 return out;
213}
214
215} // namespace microfmt::detail
216
217#if defined(MICROFMT_REFLECT_DUMP_MODE)
218
219// Three levels of indirection are required, not two: `__COUNTER__` is only
220// macro-expanded while being passed as a plain (non-`##`-adjacent)
221// argument. `MICROFMT_REFLECT_DUMP_ENUM`/`_STRUCT` pass it to a pure
222// forwarding macro first (`..._FWD`), which is what actually expands it
223// before `..._IMPL` pastes the now-numeric token -- collapsing this to two
224// levels silently pastes the literal text `__COUNTER__` instead.
225#define MICROFMT_REFLECT_DUMP_ENUM_IMPL(Type, Counter) \
226 static const bool microfmt_reflect_dump_enum_##Counter = ::microfmt::detail::register_reflect_dump_enum<Type>(#Type)
227#define MICROFMT_REFLECT_DUMP_ENUM_FWD(Type, Counter) MICROFMT_REFLECT_DUMP_ENUM_IMPL(Type, Counter)
228#define MICROFMT_REFLECT_DUMP_ENUM(Type) MICROFMT_REFLECT_DUMP_ENUM_FWD(Type, __COUNTER__)
229
230#define MICROFMT_REFLECT_DUMP_STRUCT_IMPL(Type, Counter) \
231 static const bool microfmt_reflect_dump_struct_##Counter = \
232 ::microfmt::detail::register_reflect_dump_struct<Type>(#Type)
233#define MICROFMT_REFLECT_DUMP_STRUCT_FWD(Type, Counter) MICROFMT_REFLECT_DUMP_STRUCT_IMPL(Type, Counter)
234#define MICROFMT_REFLECT_DUMP_STRUCT(Type) MICROFMT_REFLECT_DUMP_STRUCT_FWD(Type, __COUNTER__)
235
236#else // !MICROFMT_REFLECT_DUMP_MODE
237
238#define MICROFMT_REFLECT_DUMP_ENUM(Type)
239#define MICROFMT_REFLECT_DUMP_STRUCT(Type)
240
241#endif // MICROFMT_REFLECT_DUMP_MODE
242
264#define MICROFMT_REFLECT_FORMAT(Type) \
265 template <> struct microfmt::enable_reflect_format<Type> : std::true_type {}; \
266 MICROFMT_REFLECT_DUMP_STRUCT(Type)
267
268#else // !RELOCO_HAS_REFLECTION
269
270#define MICROFMT_REFLECT_FORMAT(Type)
271#define MICROFMT_REFLECT_DUMP_ENUM(Type)
272#define MICROFMT_REFLECT_DUMP_STRUCT(Type)
273
274#endif // RELOCO_HAS_REFLECTION
Definition bintime.hpp:22
Definition assert.hpp:19
Opt-in customization point for formatter<T> struct reflection.
Definition reflect_annotate.hpp:51