welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
document.hpp
Go to the documentation of this file.
1#pragma once
2#include <cstddef>
3#include <meta>
4#include <string>
5
6#include <welder/rods/python/trampoline.hpp> // overridable_virtuals / bound_flat
7
32
33namespace welder::inline v0::rods::trampolines {
34
35// --- small consteval text helpers -------------------------------------------
36
39consteval std::string int_string(std::size_t n) {
40 if (n == 0)
41 return "0";
42 std::string s{};
43 for (; n; n /= 10)
44 s.insert(s.begin(), char('0' + n % 10));
45 return s;
46}
47
60consteval bool cpp_spellable(std::meta::info ent) {
61 if (!std::meta::has_identifier(ent))
62 return false;
63 for (std::meta::info p{std::meta::parent_of(ent)};
64 p != std::meta::info{} && p != ^^::; p = std::meta::parent_of(p))
65 if (!std::meta::has_identifier(p))
66 return false;
67 return true;
68}
69
73consteval std::string cpp_qualified_name(std::meta::info ent) {
74 std::string tail{};
75 if (std::meta::has_identifier(ent))
76 tail = std::meta::identifier_of(ent);
77 for (std::meta::info p{std::meta::parent_of(ent)};
78 p != std::meta::info{} && p != ^^:: && std::meta::has_identifier(p);
79 p = std::meta::parent_of(p))
80 tail = std::string{std::meta::identifier_of(p)} + "::" + tail;
81 return "::" + tail;
82}
83
86consteval std::string trampoline_ident(std::meta::info type) {
87 std::string s{};
88 if (std::meta::has_identifier(type))
89 s = std::meta::identifier_of(type);
90 for (std::meta::info p{std::meta::parent_of(type)};
91 p != std::meta::info{} && p != ^^:: && std::meta::has_identifier(p);
92 p = std::meta::parent_of(p))
93 s = std::string{std::meta::identifier_of(p)} + "_" + s;
94 return s + "_trampoline";
95}
96
104consteval bool is_c_variadic(std::meta::info fn) {
105 std::string s{std::meta::display_string_of(std::meta::type_of(fn))};
106 return s.find("...") != std::string::npos;
107}
108
111consteval std::string qualifier_tokens(std::meta::info fn) {
112 std::string q{};
113 if (std::meta::is_const(fn))
114 q += " const";
115 if (std::meta::is_volatile(fn))
116 q += " volatile";
117 if (std::meta::is_lvalue_reference_qualified(fn))
118 q += " &";
119 if (std::meta::is_rvalue_reference_qualified(fn))
120 q += " &&";
121 if (std::meta::is_noexcept(fn))
122 q += " noexcept";
123 return q;
124}
125
127inline constexpr const char* generated_namespace{
128 "::welder::rods::trampolines::generated"};
129
137consteval std::string render_trampoline(std::meta::info type) {
138 const std::string base{cpp_qualified_name(type)};
139 const std::string ident{trampoline_ident(type)};
140 std::string s{};
141 s += "struct " + ident + " : " + base + " {\n";
142 s += " WELDER_PY_TRAMPOLINE(" + ident + ", " + base + ");\n";
143
144 std::size_t k{0};
145 for (auto slot : ::welder::overridable_virtuals(type)) {
146 const std::string name{std::meta::identifier_of(slot)};
147 if (is_c_variadic(slot)) {
148 s += " static_assert(false, \"welder: '" + base + "::" + name +
149 "' is a C-variadic virtual; C++26 reflection cannot reproduce its "
150 "'...' parameter. Mark it "
151 "[[=welder::bind_flat]] to bind it non-overridably.\");\n";
152 ++k; // keep k aligned with the overridable_virtuals index for later slots
153 continue;
154 }
155 // The reflected slot, re-derived in the generated TU (same order as here).
156 const std::string idx{"::welder::overridable_virtuals(^^" + base +
157 ")[" + int_string(k) + "]"};
158 s += " [: ::std::meta::return_type_of(" + idx + ") :] " + name + "(";
159 std::string args{};
160 std::size_t j{0};
161 for (auto p : std::meta::parameters_of(slot)) {
162 (void)p;
163 if (j) {
164 s += ", ";
165 args += ", ";
166 }
167 const std::string js{int_string(j)};
168 s += "[: ::std::meta::type_of(::std::meta::parameters_of(" + idx + ")[" +
169 js + "]) :] a" + js;
170 args += "a" + js;
171 ++j;
172 }
173 // The slot-taking macro form: dispatch keys on the slot's reflection (never
174 // `^^Base::name`, which is ill-formed for an overloaded virtual), while the
175 // textual name spells the qualified base fallback, where overload resolution
176 // picks the right overload from the forwarded parameters.
177 s += ")" + qualifier_tokens(slot) + " override { WELDER_PY_OVERRIDE_AS((" +
178 idx + "), " + name + (args.empty() ? "" : ", " + args) + "); }\n";
179 ++k;
180 }
181 s += "};\n";
182 return s;
183}
184
186consteval std::string render_registration(std::meta::info type) {
187 return "template <> constexpr ::std::meta::info\n"
188 " ::welder::rods::python::trampoline_for<" +
189 cpp_qualified_name(type) + "> = ^^" + generated_namespace +
190 "::" + trampoline_ident(type) + ";\n";
191}
192
196struct document {
197 std::string structs{};
198 std::string registrations{};
199
206 template <std::meta::info Type>
207 void add() {
208 // The generated text must SPELL the type in C++ (base clause, the
209 // trampoline_for key, the slot re-derivations) — impossible for a bare
210 // class-template specialization, which has no identifier and whose
211 // template-id cannot be respelled faithfully from reflection. The alias
212 // is that spelling: welding `using IntRing = Ring<int>;` in the swept
213 // namespace routes the carriage here with the alias as @a Type.
214 static_assert(std::meta::has_identifier(Type),
215 "welder: cannot generate a trampoline for a class-template "
216 "specialization spelled directly (it has no identifier); "
217 "declare a namespace-scope alias — using IntRing = "
218 "Ring<int>; — and weld that instead");
219 // The identifier check above covers the type itself; the chain check
220 // covers a nameable type nested inside an UNNAMEABLE scope — a virtual
221 // type declared inside a class-template specialization — whose
222 // qualified spelling would otherwise silently truncate to broken C++.
223 static_assert(
224 cpp_spellable(Type),
225 "welder: cannot generate a trampoline for a type nested inside a "
226 "class-template specialization (its enclosing scope cannot be "
227 "respelled from reflection). Hand-write the trampoline, spelling "
228 "the base through the welding alias (e.g. `struct PyValve : "
229 "IntSilo::Valve { … }` with a trampoline_for specialization), or "
230 "annotate the type [[=welder::bind_flat]].");
231 structs += std::define_static_string(render_trampoline(Type));
232 registrations += std::define_static_string(render_registration(Type));
233 }
234
236 std::string render() const {
237 std::string out{};
238 out += "#pragma once\n";
239 out += "// AUTO-GENERATED by welder (welder::rods::trampolines). Do not edit.\n";
240 out += "//\n";
241 out += "// Include AFTER your welded type headers and the active backend's\n";
242 out += "// <welder/rods/python/{pybind11,nanobind}/trampoline.hpp>.\n";
243 out += "#include <meta>\n";
244 out += "#include <welder/rods/python/trampoline.hpp>\n\n";
245 out += "namespace welder::rods::trampolines::generated {\n\n";
246 out += structs;
247 out += "\n} // namespace welder::rods::trampolines::generated\n\n";
248 out += registrations;
249 return out;
250 }
251};
252
253} // namespace welder::rods::trampolines
consteval std::string cpp_qualified_name(std::meta::info ent)
The fully ::-qualified name of a namespace-scope entity, leading :: included (geometry::Point → "geom...
Definition document.hpp:73
consteval std::string render_trampoline(std::meta::info type)
Render the trampoline struct for welded virtual type type — one override per overridable virtual,...
Definition document.hpp:137
consteval bool is_c_variadic(std::meta::info fn)
Is fn a C-style variadic function (R(…, ...))?
Definition document.hpp:104
consteval bool cpp_spellable(std::meta::info ent)
Is ent spellable as a fully-qualified C++ name — itself and every enclosing scope up to the global na...
Definition document.hpp:60
consteval std::string int_string(std::size_t n)
Render a non-negative integer as decimal (constexpr std::to_string is not available on gcc-16 in a co...
Definition document.hpp:39
constexpr const char * generated_namespace
The namespace the generated trampoline structs are emitted into (fully qualified).
Definition document.hpp:127
consteval std::string render_registration(std::meta::info type)
The trampoline_for<type> specialization pointing at type's generated struct.
Definition document.hpp:186
consteval std::string trampoline_ident(std::meta::info type)
A collision-free identifier for type's generated trampoline: its qualified name with :: folded to _ (...
Definition document.hpp:86
consteval std::string qualifier_tokens(std::meta::info fn)
The trailing cv / ref-qualifier / noexcept tokens of a member function fn, in grammatical order (" co...
Definition document.hpp:111
consteval std::vector< std::meta::info > overridable_virtuals(std::meta::info type)
Every overridable virtual slot of type — the ones welder routes through a trampoline — folding in vir...
Definition virtuals.hpp:174
The growing generated header: the trampoline struct bodies and their trampoline_for registrations,...
Definition document.hpp:196
std::string structs
Accumulated trampoline struct definitions.
Definition document.hpp:197
std::string render() const
The finished, self-contained header text.
Definition document.hpp:236
void add()
Append the generated trampoline + registration for welded virtual Type.
Definition document.hpp:207
std::string registrations
Accumulated trampoline_for specializations.
Definition document.hpp:198
Virtual-function overriding support shared by welder's Python backends.