welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
rod.hpp
Go to the documentation of this file.
1#pragma once
46
47#include <cstddef>
48#include <meta>
49#include <ostream>
50#include <string>
51#include <type_traits>
52#include <utility>
53#include <vector>
54
55#include <welder/welder.hpp> // welder::welder + rod contract + driver
56#include <welder/rods/lua/luacats/document.hpp> // the document + writer handles
57#include <welder/rods/lua/luacats/type_map.hpp> // lua_type / operator map / text
58#include <welder/bind_traits.hpp> // aggregate_fields / is_unary_operator
59#include <welder/doc.hpp> // doc_of / param_docs / return_doc_of
60
61namespace welder::inline v0::rods::luacats {
62
70struct rod {
71 static constexpr lang language{lang::lua};
73
78 template <class> using class_handle_type = class_writer;
79 template <class> using enum_handle_type = enum_writer;
80
81 struct session {};
82
83 protected:
84 // --- implementation helpers (not part of the welder::rod contract) --
85
88 template <auto Bases, std::size_t... I>
89 static consteval const char* _bases_string(std::index_sequence<I...>) {
90 std::string out{};
91 ((out += (out.empty() ? "" : ", ") + qualified_name(Bases[I])), ...);
92 return std::define_static_string(out);
93 }
94
102 template <class T, std::size_t... J>
103 static void _aggregate_param_lines(std::string& out, std::string& args,
104 std::index_sequence<J...>) {
105 static constexpr auto fields{::welder::detail::aggregate_fields<T>()};
106 static constexpr const char* names[]{
107 std::define_static_string(std::meta::identifier_of(fields[J]))...};
108 static constexpr const char* types[]{
109 lua_type(std::meta::type_of(fields[J]))...};
110 for (std::size_t i{0}; i < sizeof...(J); ++i) {
111 out += "---@param ";
112 out += names[i];
113 out += ' ';
114 out += types[i];
115 out += '\n';
116 args += (args.empty() ? "" : ", ");
117 args += names[i];
118 }
119 }
120
123 template <class T, std::size_t... J>
124 static std::string _aggregate_fun_params(std::index_sequence<J...>) {
125 static constexpr auto fields{::welder::detail::aggregate_fields<T>()};
126 static constexpr const char* names[]{
127 std::define_static_string(std::meta::identifier_of(fields[J]))...};
128 static constexpr const char* types[]{
129 lua_type(std::meta::type_of(fields[J]))...};
130 std::string s{};
131 for (std::size_t i{0}; i < sizeof...(J); ++i) {
132 if (i)
133 s += ", ";
134 s += names[i];
135 s += ": ";
136 s += types[i];
137 }
138 return s;
139 }
140
141 public:
142 // --- caster oracle + emission primitives (the welder::rod contract) --
143
146 template <class T>
147 static constexpr bool has_native_caster =
149
152 static consteval const char* special_method_name(std::meta::info op_fn) {
153 return operator_luacats(op_fn);
154 }
155
156 // --- class binding ------------------------------------------------------
157
160 template <class T, auto Bases, std::size_t... I>
161 static class_writer make_class(module_type& m, const char* name,
162 const char* doc, std::index_sequence<I...> seq) {
163 return make_class<T, ^^T, Bases>(m, name, doc, seq);
164 }
165
172 template <class T, std::meta::info Decl, auto Bases, std::size_t... I>
173 static class_writer make_class(module_type& m, const char* name,
174 const char* doc, std::index_sequence<I...> seq) {
175 class_writer w{};
176 w.doc = m.doc;
177 w.qualified = m.prefix.empty() ? std::string{name}
178 : m.prefix + "." + name;
179 w.cls_doc = doc ? doc : "";
181 // Register raw C++ name -> this styled/weld_as declaration, so references to
182 // T elsewhere (fields, params, returns, bases, containers) are reconciled at
183 // render(). qualified_name(Decl) is exactly what the type map emits for them.
184 m.doc->record_type_name(std::define_static_string(qualified_name(Decl)),
185 w.qualified);
186 return w;
187 }
188
195 template <class T, auto Bases, std::size_t... I>
197 const char* name, const char* doc,
198 std::index_sequence<I...> seq) {
199 return make_nested_class<T, ^^T, Bases>(m, outer, name, doc, seq);
200 }
201
209 template <class T, std::meta::info Decl, auto Bases, std::size_t... I>
211 const char* name, const char* doc,
212 std::index_sequence<I...> seq) {
213 class_writer w{};
214 w.doc = m.doc;
215 w.sink = &outer.trailing;
216 w.qualified = outer.qualified + "." + name;
217 w.cls_doc = doc ? doc : "";
219 // The rename key must be what the type map emits for REFERENCES to T:
220 // the target's own dotted name when it has one (a member-alias target
221 // declared elsewhere — `vendor.Plate` — so its references remap to this
222 // declaration), else the alias Decl's (an unnameable specialization,
223 // whose qualified_name(^^T) would collapse to the bare enclosing scope
224 // and corrupt the rename table). For a declared nested type the two
225 // coincide and the record is a no-op unless a style/weld_as renames.
227 std::define_static_string(std::meta::has_identifier(^^T)
228 ? qualified_name(^^T)
229 : qualified_name(Decl)),
230 w.qualified);
231 return w;
232 }
233
238 template <class T, auto Ctors, bool HasDefault, bool Aggregate>
240 if constexpr (HasDefault) {
241 func_overload o{};
242 o.ret_line = "---@return " + w.qualified + '\n';
243 o.fun_sig = "fun(): " + w.qualified;
244 w.ctors.push_back(std::move(o));
245 }
246 template for (constexpr auto ctor : std::define_static_array(Ctors)) {
247 w.ctors.push_back(build_overload<ctor>(w.qualified));
248 }
249 if constexpr (Aggregate) {
250 // C++26 aggregate field constructor: one `.new` param per field.
251 static constexpr auto seq{std::make_index_sequence<
253 func_overload o{};
255 o.ret_line = "---@return " + w.qualified + '\n';
256 o.fun_sig = "fun(" + _aggregate_fun_params<T>(seq) + "): " + w.qualified;
257 w.ctors.push_back(std::move(o));
258 }
259 }
260
263 template <std::meta::info Mem, class Style = ::welder::naming::none>
264 static void add_field(class_writer& w) {
265 w.fields += "---@field ";
267 w.fields += ' ';
268 w.fields += lua_type(std::meta::type_of(Mem));
269 std::string d{one_line(::welder::doc_of<Mem>())};
270 // LuaCATS has no read-only/const field modifier (an open feature request on
271 // lua-language-server), so a const member's immutability — which the sol2
272 // runtime backend enforces with sol::readonly — is surfaced as a description
273 // note rather than an (unrecognized) tag.
274 if constexpr (std::meta::is_const_type(std::meta::type_of(Mem)))
275 d = d.empty() ? "(read-only)" : d + " (read-only)";
276 if (!d.empty())
277 w.fields += ' ' + d;
278 w.fields += '\n';
279 }
280
284 template <auto Fns, class Style = ::welder::naming::none>
285 static void add_method(class_writer& w) {
286 // A method is `Class:name(...)` (implicit self); reflection's parameters
287 // already exclude self, so the arg list renders directly.
288 std::vector<func_overload> sigs{};
289 collect_overloads<Fns>(sigs, std::make_index_sequence<Fns.size()>{});
291 w.methods,
292 w.qualified + ":" +
293 ::welder::name_of<Fns[0], language, Style,
295 sigs);
296 }
297
300 template <auto Fns, class Style = ::welder::naming::none>
302 std::vector<func_overload> sigs{};
303 collect_overloads<Fns>(sigs, std::make_index_sequence<Fns.size()>{});
305 w.methods,
306 w.qualified + "." +
307 ::welder::name_of<Fns[0], language, Style,
309 sigs);
310 }
311
315 template <auto Fns>
316 static void add_operator(class_writer& w) {
317 template for (constexpr auto fn : std::define_static_array(Fns)) {
318 const char* op{operator_luacats(fn)};
319 w.fields += "---@operator ";
320 w.fields += op;
321 if constexpr (!::welder::detail::is_unary_operator(fn)) {
322 constexpr auto types{param_lua_types<fn>()};
323 w.fields += "(";
324 w.fields += types[0];
325 w.fields += ")";
326 }
327 using R = [:std::meta::return_type_of(fn):];
328 if constexpr (!std::is_void_v<R>) {
329 w.fields += ": ";
330 w.fields += lua_type(std::meta::return_type_of(fn));
331 }
332 w.fields += '\n';
333 }
334 }
335
336 // --- enum binding -------------------------------------------------------
337
340 template <class E>
341 static enum_writer make_enum(module_type& m, const char* name,
342 const char* doc) {
343 enum_writer w{};
344 w.doc = m.doc;
345 w.qualified = m.prefix.empty() ? std::string{name}
346 : m.prefix + "." + name;
347 w.enum_doc = doc ? doc : "";
348 // Register raw -> styled so references to E (e.g. a field of enum type) are
349 // reconciled at render(); see the same call in make_class.
350 m.doc->record_type_name(std::define_static_string(qualified_name(^^E)),
351 w.qualified);
352 return w;
353 }
354
358 template <class E>
360 const char* name, const char* doc) {
361 enum_writer w{};
362 w.doc = m.doc;
363 w.sink = &outer.trailing;
364 w.qualified = outer.qualified + "." + name;
365 w.enum_doc = doc ? doc : "";
366 m.doc->record_type_name(std::define_static_string(qualified_name(^^E)),
367 w.qualified);
368 return w;
369 }
370
372 template <std::meta::info Enum, class Style = ::welder::naming::none>
373 static void add_enumerator(enum_writer& w) {
374 w.values += " ";
375 w.values +=
377 w.values += " = ";
378 constexpr long long v{static_cast<long long>(std::to_underlying([:Enum:]))};
379 w.values += std::to_string(v);
380 w.values += ",\n";
381 }
382
384 template <class E>
385 static void finish_enum(enum_writer&) {} // RAII flush handles it
386
387 // --- namespace / module binding -----------------------------------------
388
390 static session open_module(module_type&) { return {}; }
391
393 static void set_module_doc(module_type& m, const char* doc) {
395 }
396
401 template <auto Fns, class Style = ::welder::naming::none>
402 static void add_function(module_type& m, const char* name = nullptr) {
403 std::vector<func_overload> sigs{};
404 collect_overloads<Fns>(sigs, std::make_index_sequence<Fns.size()>{});
406 m.doc->body,
407 (m.prefix.empty() ? std::string{} : m.prefix + ".") +
408 ::welder::name_of_or<Fns[0], language, Style,
410 sigs);
411 }
412
415 template <std::meta::info Var, class Style = ::welder::naming::none>
416 static void add_variable(module_type& m, session&, const char* name = nullptr) {
417 std::string& out{m.doc->body};
419 out += "---@type ";
420 out += lua_type(std::meta::type_of(Var));
421 out += '\n';
422 out += (m.prefix.empty() ? std::string{} : m.prefix + ".") +
423 ::welder::name_of_or<Var, language, Style,
425 " = nil\n\n";
426 }
427
429 static module_type add_submodule(module_type& m, const char* name) {
430 const std::string prefix{m.prefix.empty() ? std::string{name}
431 : m.prefix + "." + name};
432 m.doc->declare_table(prefix);
433 return module_type{m.doc, prefix};
434 }
435
438
439 // --- whole-stub generation (this backend's extra entry point) -----------
440
461 template <std::meta::info Ns, class Style = ::welder::naming::none>
462 static void generate(std::ostream& os) {
463 static_assert(std::meta::is_namespace(Ns),
464 "welder: luacats::generate<Ns>: Ns must reflect a namespace");
465 document doc{};
467 std::define_static_string(qualified_name(Ns))};
468 doc.declare_table(m.prefix, ::welder::doc_of<Ns>());
469 ::welder::welder<rod, Style>::template weld_namespace<Ns>(m);
470 os << doc.render();
471 }
472};
473
474static_assert(::welder::rod<rod>,
475 "welder::rods::luacats::rod must satisfy welder::rod");
476
477} // namespace welder::rods::luacats
Backend-agnostic selection layer: the reflection predicates and selectors that decide what participat...
The contract a rod (a welder backend, welder::rods::…::rod) must satisfy to plug into the generic dri...
Definition concepts.hpp:206
Language-agnostic documentation layer: read [[=welder::doc(...)]] annotations off reflected entities ...
The LuaCATS document assembler: how a ---@meta stub is built up in memory.
consteval auto aggregate_fields()
The fields an aggregate is initialized from: its non-static data members in declaration order (all pu...
consteval bool is_unary_operator(std::meta::info f)
Whether a member operator is unary (0 parameters) vs binary (1 parameter).
void render_overload_group(std::string &out, const std::string &callee, const std::vector< func_overload > &sigs)
Emit one documented function <callee>(…) for overload group sigs, with a ---@overload fun(…) line per...
Definition document.hpp:189
constexpr bool is_native_lua
Whether the backend converts U without welder registering a type: scalars, strings and char*.
Definition type_map.hpp:189
consteval auto param_lua_types()
The LuaCATS type name of each parameter of Fn, as a splice-ready array parallel to param_docs<Fn>().
Definition document.hpp:44
consteval const char * lua_type(std::meta::info type)
lua_type_string as a static-storage C string, callable on a constant type reflection.
Definition type_map.hpp:180
consteval std::string qualified_name(std::meta::info ent)
The dotted LuaCATS name of a namespace-or-type reflection: its own identifier prefixed by each enclos...
Definition type_map.hpp:71
std::string one_line(const char *text)
A one-line description for a @field/@param/@return tail: newlines in text collapse to spaces (LuaCATS...
Definition type_map.hpp:52
func_overload build_overload(const std::string &ret_override)
Build the func_overload for function/constructor Fn.
Definition document.hpp:146
void collect_overloads(std::vector< func_overload > &out, std::index_sequence< I... >)
Append build_overload for each member of overload group Grp to out.
Definition document.hpp:174
void emit_doc_comment(std::string &out, const char *text)
Append text as --- comment lines (each source line prefixed), so a multiline summary lands as a LuaC...
Definition type_map.hpp:36
consteval const char * operator_luacats(std::meta::info f)
A member operator's LuaCATS ---@operator name, or nullptr if not rendered.
Definition type_map.hpp:219
consteval detail::doc_spec< N > doc(const char(&s)[N])
Attach a docstring to a namespace, class, function, or function parameter.
lang
The target languages welder ships rods for — but not the whole value space.
Definition lang.hpp:42
@ lua
Lua (via the sol2 and LuaBridge3 backends).
Definition lang.hpp:44
@ static_method
a static member function → transform_static_method.
Definition naming.hpp:244
@ function
a free function → transform_function.
Definition naming.hpp:245
@ variable
a namespace variable → transform_variable.
Definition naming.hpp:247
@ method
a member function → transform_method.
Definition naming.hpp:243
constexpr const char * name_of_or(const char *override_)
Resolve a bound name with a call-site override: override_ wins verbatim, nullptr falls back to name_o...
Definition naming.hpp:343
consteval const char * name_of()
The final bound name of Ent (a K-kind entity) for language L under name style Style.
Definition naming.hpp:294
consteval const char * doc_of()
The doc text on Ent (a class, namespace, function, or parameter), or nullptr.
Definition doc.hpp:138
std::string qualified
Dotted LuaCATS class name.
Definition document.hpp:350
std::vector< func_overload > ctors
The .new overloads, grouped on flush.
Definition document.hpp:356
std::string * sink
Flush target override; null = doc->body.
Definition document.hpp:344
std::string bases
Comma-joined base names, or empty.
Definition document.hpp:352
std::string fields
Accumulated ---@field / ---@operator lines.
Definition document.hpp:353
std::string methods
Accumulated function statements.
Definition document.hpp:354
std::string cls_doc
The class doc.
Definition document.hpp:351
std::string trailing
Nested types' flushed blocks (after methods).
Definition document.hpp:355
The growing LuaCATS document shared by every writer handle.
Definition document.hpp:270
void declare_table(std::string_view prefix, const char *doc=nullptr)
Record (once) a module table to declare, updating its doc if given.
Definition document.hpp:281
void record_type_name(std::string_view raw, std::string_view styled)
Register a welded type's declared name so references to it are reconciled at render.
Definition document.hpp:297
An enum handle: the value table text accumulated by add_enumerator, flushed as a ---@enum block by RA...
Definition document.hpp:397
std::string * sink
Flush target override; null = doc->body (a nested enum flushes into its outer's trailing,...
Definition document.hpp:399
std::string values
Accumulated Name = value, lines.
Definition document.hpp:404
One overload of a callee, pre-rendered into the pieces a group needs: the summary doc,...
Definition document.hpp:132
std::string args
The function callee(<args>) name list.
Definition document.hpp:136
std::string ret_line
The ---@return … line (empty when void).
Definition document.hpp:135
std::string fun_sig
The fun(a: T, …): R signature.
Definition document.hpp:137
std::string params
---@param … lines (may be empty).
Definition document.hpp:134
A module handle: the shared document plus this (sub)module's dotted table path.
Definition document.hpp:332
No deferred module state.
Definition rod.hpp:81
static void add_function(module_type &m, const char *name=nullptr)
Emit free-function overload group Fns as one documented module-level function (further overloads as -...
Definition rod.hpp:402
static enum_writer make_nested_enum(module_type &m, class_writer &outer, const char *name, const char *doc)
Open the enum table declaration for a nested member enum, declared under the OUTER's dotted name and ...
Definition rod.hpp:359
static class_writer make_class(module_type &m, const char *name, const char *doc, std::index_sequence< I... > seq)
Open a ---@class block for T and register its raw→styled name for later type-reference reconciliation...
Definition rod.hpp:161
class_writer class_handle_type
The class / enum handles the per-class / per-enum hooks write into — exactly what make_class / make_e...
Definition rod.hpp:78
static class_writer make_class(module_type &m, const char *name, const char *doc, std::index_sequence< I... > seq)
The declaring-entity-aware form the carriage prefers: Decl is ^^T, or the namespace-scope alias a cla...
Definition rod.hpp:173
static consteval const char * special_method_name(std::meta::info op_fn)
Member operator → its ---@operator name, or nullptr (also gates eligibility, like every backend).
Definition rod.hpp:152
static module_type add_submodule(module_type &m, const char *name)
Declare a nested submodule table under m and return its writer.
Definition rod.hpp:429
static constexpr bool has_native_caster
is_native_lua drives the shared bindability gate.
Definition rod.hpp:147
static class_writer make_nested_class(module_type &m, class_writer &outer, const char *name, const char *doc, std::index_sequence< I... > seq)
Open a ---@class block for a nested member type, declared under the OUTER's dotted name (mod....
Definition rod.hpp:196
static void finish_enum(enum_writer &)
No finalizer needed — the enum_writer flushes on destruction.
Definition rod.hpp:385
static enum_writer make_enum(module_type &m, const char *name, const char *doc)
Open an enum table declaration for E and register its raw→styled name.
Definition rod.hpp:341
static void close_module(module_type &, session &)
Close the session (no-op; the document accumulates directly).
Definition rod.hpp:437
static class_writer make_nested_class(module_type &m, class_writer &outer, const char *name, const char *doc, std::index_sequence< I... > seq)
The declaring-entity-aware nested form the carriage prefers: Decl is ^^T, or the member alias an (oth...
Definition rod.hpp:210
static void add_variable(module_type &m, session &, const char *name=nullptr)
Emit a ---@type-annotated <name> = nil module variable declaration.
Definition rod.hpp:416
static void set_module_doc(module_type &m, const char *doc)
Declare the (sub)module table, carrying doc as its comment.
Definition rod.hpp:393
static void add_operator(class_writer &w)
Emit operator overload group Fns as ---@operator name(rhs): R lines on the class block (one line per ...
Definition rod.hpp:316
module_writer module_type
Definition rod.hpp:72
static void add_enumerator(enum_writer &w)
Emit an Name = <int> line for enumerator Enum.
Definition rod.hpp:373
static void add_constructors(class_writer &w)
Emit the whole .new(…) constructor overload set (the driver-passed pieces: a no-argument overload whe...
Definition rod.hpp:239
static void add_field(class_writer &w)
Emit a ---@field line for data member Mem (const → a (read-only) note, since LuaCATS has no const mod...
Definition rod.hpp:264
static session open_module(module_type &)
Open a per-module session (no deferred state).
Definition rod.hpp:390
static void generate(std::ostream &os)
Emit the LuaCATS ---@meta stub for top-level namespace Ns to os.
Definition rod.hpp:462
static constexpr lang language
Stubs are for the Lua binding.
Definition rod.hpp:71
enum_writer enum_handle_type
Definition rod.hpp:79
static consteval const char * _bases_string(std::index_sequence< I... >)
The comma-joined LuaCATS names of a class's welded bases (for ---@class X : …).
Definition rod.hpp:89
static void add_method(class_writer &w)
Emit method overload group Fns as one documented Class:name(…) (further overloads as ---@overload lin...
Definition rod.hpp:285
static void _aggregate_param_lines(std::string &out, std::string &args, std::index_sequence< J... >)
Precompute an aggregate's ---@param lines and its .new argument list.
Definition rod.hpp:103
static std::string _aggregate_fun_params(std::index_sequence< J... >)
The <field>: <type>, … list for an aggregate's .new fun(…) signature (its ---@overload line).
Definition rod.hpp:124
static void add_static_method(class_writer &w)
Emit static-method overload group Fns as a documented Class.name(…) (dotted, no self).
Definition rod.hpp:301
welder's binding entry point, parameterized on a rod.
Definition welder.hpp:85
The low-level rendering primitives of the LuaCATS stub backend: the C++→LuaCATS type map**,...
welder's binding entry point: the welder::welder struct.