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
73#include <array>
74#include <cstddef>
75#include <meta>
76#include <string>
77#include <type_traits>
78#include <utility>
79#include <vector>
80
81#include <welder/welder.hpp> // welder::welder + rod contract + driver
82#include <welder/rods/lua/metamethods.hpp> // shared operator -> Lua __name map
83#include <welder/bind_traits.hpp> // aggregate_* helpers
84
85// LuaBridge3 requires the Lua headers to be visible first (its Config.h hard-errors
86// otherwise). Pull them in here so a binding TU only needs this rod header, the way
87// the sol2 rod gets Lua transitively through <sol/sol.hpp>. lua.hpp is the C++
88// convenience wrapper every PUC-Lua / LuaJIT distribution ships.
89#include <lua.hpp>
90
91#include <LuaBridge/LuaBridge.h>
92
93// Make every C++ enum convert to/from a Lua integer. By default LuaBridge3 treats
94// an unspecialized type as userdata, so an enum-typed function parameter/return
95// would demand a registered userdata; welder instead binds an enum as a table of
96// integer values (see make_enum), so its values cross the boundary as integers.
97// This blanket specialization — the generalization of LuaBridge3's opt-in
98// `Stack<E> = Enum<E>` — makes that work for any welded enum without per-enum glue.
99namespace luabridge {
100template <class E>
101struct Stack<E, std::enable_if_t<std::is_enum_v<E>>> : Enum<E> {};
102} // namespace luabridge
103
104namespace welder::inline v0::rods::luabridge {
105
106// The welder rod namespace `welder::rods::luabridge` would shadow the library's
107// `::luabridge`, so alias it (as the Python rods alias pybind11/nanobind) and use
108// `lb::` throughout.
109namespace lb = ::luabridge;
110
112
116template <class... A>
117using ctor_sig = void(A...);
118
124template <class T, class... A>
125T make_object(A... args) {
126 return T(std::move(args)...);
127}
128
139struct rod {
140 static constexpr lang language{lang::lua};
141
150 lua_State* L{nullptr};
151 std::vector<std::string> path{};
152 };
154
159 struct session {};
160
164 template <class T>
166 using type = T;
168 std::string name;
169 };
170
179 struct enum_handle {
181 std::string name;
182 bool scoped;
183 std::string outer_key{};
184 };
185
190 template <class T> using class_handle_type = class_handle<T>;
191 template <class> using enum_handle_type = enum_handle;
192
193 protected:
194 // --- implementation helpers (not part of the welder::rod contract) --
195
210 template <class T>
211 static constexpr bool _needs_registration =
212 std::is_enum_v<std::remove_cvref_t<T>> ||
213 lb::detail::IsUserdata<std::remove_cvref_t<T>>::value;
214
216 template <class H>
217 using _class_type = typename std::remove_cvref_t<H>::type;
218
224 static lb::Namespace _open_namespace(const module_scope& m) {
225 lb::Namespace ns{lb::getGlobalNamespace(m.L)};
226 for (const auto& seg : m.path)
227 ns = ns.beginNamespace(seg.c_str());
228 return ns;
229 }
230
236 template <class T>
237 static auto _open_class(const module_scope& m, const char* name) {
238 return _open_namespace(m).template beginClass<T>(name);
239 }
240
249 static void _push_module_table(const module_scope& m) {
250 lua_pushglobaltable(m.L);
251 for (const auto& seg : m.path) {
252 lua_pushlstring(m.L, seg.data(), seg.size());
253 lua_rawget(m.L, -2);
254 lua_remove(m.L, -2);
255 }
256 }
257
263 template <class T, auto Ctors, bool HasDefault, bool Aggregate>
264 static consteval std::vector<std::vector<std::meta::info>> _ctor_arg_lists() {
265 std::vector<std::vector<std::meta::info>> lists;
266 if (HasDefault)
267 lists.push_back({}); // ()
268 for (auto c : Ctors) {
269 std::vector<std::meta::info> args;
270 for (auto p : std::meta::parameters_of(c))
271 args.push_back(std::meta::type_of(p));
272 lists.push_back(args);
273 }
274 if (Aggregate) {
275 std::vector<std::meta::info> args;
277 args.push_back(std::meta::type_of(fld));
278 lists.push_back(args);
279 }
280 return lists;
281 }
282
285 template <class T, auto Ctors, bool HasDefault, bool Aggregate>
286 static consteval auto _ctor_sigs_array() {
287 constexpr std::size_t n{
289 std::array<std::meta::info, n> out{};
290 if constexpr (n != 0) {
292 for (std::size_t i{0}; i < n; ++i)
293 out[i] = std::meta::dealias(std::meta::substitute(^^ctor_sig, lists[i]));
294 }
295 return out;
296 }
297
300 template <class T, auto Ctors, bool HasDefault, bool Aggregate>
301 static consteval auto _factory_array() {
302 constexpr std::size_t n{
304 std::array<std::meta::info, n> out{};
305 if constexpr (n != 0) {
307 for (std::size_t i{0}; i < n; ++i) {
308 std::vector<std::meta::info> targs{^^T};
309 for (auto a : lists[i])
310 targs.push_back(a);
311 out[i] = std::meta::substitute(^^make_object, targs);
312 }
313 }
314 return out;
315 }
316
321 template <auto Sigs, auto Factories, class Cls, std::size_t... I>
322 static void _add_constructors(Cls& cls, std::index_sequence<I...>) {
323 cls.template addConstructor<typename [:Sigs[I]:]...>();
324 cls.addStaticFunction("new", &[:Factories[I]:]...);
325 }
326
331 template <class T, auto Bases, std::size_t... I>
332 static void _make_class(const module_scope& m, const char* name,
333 std::index_sequence<I...>) {
334 if constexpr (sizeof...(I) == 0) {
335 auto cls{_open_namespace(m).template beginClass<T>(name)};
336 } else {
337 auto cls{_open_namespace(m)
338 .template deriveClass<T, typename [:Bases[I]:]...>(name)};
339 }
340 }
341
347 template <auto Grp, class Target, std::size_t... I>
348 static void _add_function(Target& t, const char* name,
349 std::index_sequence<I...>) {
350 // LuaBridge3 owns a returned object structurally (a value → a Lua-owned
351 // copy; a pointer/reference → a non-owning view), so a
352 // [[=welder::return_policy]] has no runtime effect here — but a
353 // self-contradictory one (a reference to a returned temporary) is still
354 // rejected, uniformly with the Python rods.
356 t.addFunction(name, &[:Grp[I]:]...);
357 }
358
360 template <auto Grp, class Target, std::size_t... I>
361 static void _add_static_function(Target& t, const char* name,
362 std::index_sequence<I...>) {
364 t.addStaticFunction(name, &[:Grp[I]:]...);
365 }
366
367 public:
368 // --- caster oracle + emission primitives (the welder::rod contract) --
369
374 template <class T>
376
379 static consteval const char* special_method_name(std::meta::info op_fn) {
380 return lua_metamethod_name(op_fn);
381 }
382
383 // --- class binding ------------------------------------------------------
384
389 template <class T, auto Bases, std::size_t... I>
390 static class_handle<T> make_class(module_type& m, const char* name,
391 const char* /*doc*/,
392 std::index_sequence<I...> /*seq*/) {
393 _make_class<T, Bases>(m, name, std::make_index_sequence<Bases.size()>{});
394 return class_handle<T>{m, std::string{name}};
395 }
396
406 template <class T, auto Bases, std::size_t... I>
408 const char* name, const char* /*doc*/,
409 std::index_sequence<I...> /*seq*/) {
410 std::string temp{outer.name + "." + name};
411 _make_class<T, Bases>(outer.mod, temp.c_str(),
412 std::make_index_sequence<Bases.size()>{});
413 return class_handle<T>{outer.mod, std::move(temp)};
414 }
415
423 template <class T>
424 static void finish_nested_class(module_type&, auto& outer, auto& cls,
425 const char* name) {
426 lua_State* L{cls.mod.L};
427 _push_module_table(cls.mod); // [mod]
428 lua_pushlstring(L, cls.name.data(), cls.name.size());
429 lua_rawget(L, -2); // [mod, inner]
430 lua_pushlstring(L, outer.name.data(), outer.name.size());
431 lua_rawget(L, -3); // [mod, inner, outerT]
432 lua_pushstring(L, name);
433 lua_pushvalue(L, -3); // [.., outerT, name, inner]
434 lua_rawset(L, -3); // outerT[name] = inner
435 lua_pop(L, 2); // [mod]
436 lua_pushlstring(L, cls.name.data(), cls.name.size());
437 lua_pushnil(L);
438 lua_rawset(L, -3); // mod["Outer.Inner"] = nil
439 lua_pop(L, 1);
440 }
441
446 template <class T, auto Ctors, bool HasDefault, bool Aggregate>
447 static void add_constructors(auto& h) {
449 constexpr auto factories{_factory_array<T, Ctors, HasDefault, Aggregate>()};
450 if constexpr (sigs.size() != 0) {
451 auto cls{_open_class<T>(h.mod, h.name.c_str())};
453 cls, std::make_index_sequence<sigs.size()>{});
454 }
455 }
456
466 template <std::meta::info Mem, class Style = ::welder::naming::none>
467 static void add_field(auto& h) {
468 using T = _class_type<decltype(h)>;
469 constexpr const char* name{
471 auto cls{_open_class<T>(h.mod, h.name.c_str())};
472 if constexpr (!std::meta::is_public(Mem)) {
473 // A protected member (admitted under policy::weld_protected) binds
474 // as a getter/setter property over welder::detail::field_access —
475 // gcc-16 rejects the dependent `&[:Mem:]` for protected data (see
476 // field_access).
478 if constexpr (std::meta::is_const_type(std::meta::type_of(Mem)))
479 cls.addProperty(name, &fa::get);
480 else
481 cls.addProperty(name, &fa::get, &fa::set);
482 } else {
483 using Field = typename [:std::meta::type_of(Mem):];
484 constexpr Field T::* mp{&[:Mem:]};
485 if constexpr (std::meta::is_const_type(std::meta::type_of(Mem)))
486 cls.addProperty(name, mp); // read-only (const member)
487 else
488 cls.addProperty(name, mp, mp); // read/write
489 }
490 }
491
495 template <auto Fns, class Style = ::welder::naming::none>
496 static void add_method(auto& h) {
497 using T = _class_type<decltype(h)>;
498 auto cls{_open_class<T>(h.mod, h.name.c_str())};
500 cls,
502 std::make_index_sequence<Fns.size()>{});
503 }
504
507 template <auto Fns, class Style = ::welder::naming::none>
508 static void add_static_method(auto& h) {
509 using T = _class_type<decltype(h)>;
510 auto cls{_open_class<T>(h.mod, h.name.c_str())};
512 cls,
513 ::welder::name_of<Fns[0], language, Style,
515 std::make_index_sequence<Fns.size()>{});
516 }
517
540 template <auto Fns>
541 static void add_operator(auto& h) {
542 using T = _class_type<decltype(h)>;
543 constexpr auto Fn{Fns[0]};
544 {
545 auto cls{_open_class<T>(h.mod, h.name.c_str())};
546 if constexpr (std::meta::operator_of(Fn) ==
547 std::meta::operators::op_square_brackets) {
548 using Key = std::remove_cvref_t<typename [:std::meta::type_of(
549 std::meta::parameters_of(Fn)[0]):]>;
550 cls.addIndexMetaMethod(
551 +[](T& self, const lb::LuaRef& key, lua_State* L) -> lb::LuaRef {
552 if constexpr (std::is_arithmetic_v<Key>) {
553 key.push(L); // may be a stringified number (see above)
554 int is_num{0};
555 const lua_Number n{lua_tonumberx(L, -1, &is_num)};
556 lua_pop(L, 1);
557 if (!is_num)
558 return lb::LuaRef(L); // not a subscript key
559 return lb::LuaRef(L, self[static_cast<Key>(n)]);
560 } else {
561 if (auto k = key.template cast<Key>())
562 return lb::LuaRef(L, self[*k]);
563 return lb::LuaRef(L); // nil: fall through to member lookup
564 }
565 });
566 } else {
567 constexpr const char* slot{lua_metamethod_name(Fn)};
568 _add_function<Fns>(cls, slot, std::make_index_sequence<Fns.size()>{});
569 }
570 }
571 }
572
573 // --- enum binding -------------------------------------------------------
574
577 template <class E>
578 static enum_handle make_enum(module_type& m, const char* name,
579 const char* /*doc*/) {
580 _open_namespace(m).beginNamespace(name); // create (empty), then unwind
581 return enum_handle{m, std::string{name}, std::is_scoped_enum_v<E>};
582 }
583
590 template <class E>
592 const char* name, const char* /*doc*/) {
593 lua_State* L{outer.mod.L};
594 _push_module_table(outer.mod); // [mod]
595 lua_pushlstring(L, outer.name.data(), outer.name.size());
596 lua_rawget(L, -2); // [mod, outerT]
597 lua_pushstring(L, name);
598 lua_newtable(L); // [.., name, values]
599 lua_rawset(L, -3); // outerT[name] = {}
600 lua_pop(L, 2);
601 return enum_handle{outer.mod, std::string{name}, std::is_scoped_enum_v<E>,
602 outer.name};
603 }
604
610 template <std::meta::info Enum, class Style = ::welder::naming::none>
611 static void add_enumerator(enum_handle& e) {
612 constexpr const char* name{
614 const lua_Integer value{static_cast<lua_Integer>(std::to_underlying([:Enum:]))};
615 if (e.outer_key.empty()) {
616 _open_namespace(e.mod).beginNamespace(e.name.c_str()).addVariable(name, value);
617 if (!e.scoped)
618 _open_namespace(e.mod).addVariable(name, value);
619 } else {
620 // Class-nested: write through the outer's table with raw ops (the
621 // value table is a raw static entry created by make_nested_enum).
622 lua_State* L{e.mod.L};
623 _push_module_table(e.mod); // [mod]
624 lua_pushlstring(L, e.outer_key.data(), e.outer_key.size());
625 lua_rawget(L, -2); // [mod, outerT]
626 lua_pushlstring(L, e.name.data(), e.name.size());
627 lua_rawget(L, -2); // [mod, outerT, values]
628 lua_pushstring(L, name);
629 lua_pushinteger(L, value);
630 lua_rawset(L, -3); // values[name] = v
631 lua_pop(L, 1); // [mod, outerT]
632 if (!e.scoped) {
633 lua_pushstring(L, name);
634 lua_pushinteger(L, value);
635 lua_rawset(L, -3); // outerT[name] = v
636 }
637 lua_pop(L, 2);
638 }
639 }
640
643 template <class /*E*/>
644 static void finish_enum(auto&) {}
645
646 // --- namespace / module binding -----------------------------------------
647
649 static session open_module(module_type&) { return {}; }
650
653 static void set_module_doc(module_type&, const char*) {}
654
659 template <auto Fns, class Style = ::welder::naming::none>
660 static void add_function(module_type& m, const char* name = nullptr) {
661 lb::Namespace ns{_open_namespace(m)};
663 ns,
664 ::welder::name_of_or<Fns[0], language, Style,
666 std::make_index_sequence<Fns.size()>{});
667 }
668
676 template <std::meta::info Var, class Style = ::welder::naming::none>
677 static void add_variable(module_type& m, session& /*s*/,
678 const char* name = nullptr) {
679 const char* key{::welder::name_of_or<Var, language, Style,
681 lb::Namespace ns{_open_namespace(m)};
682 if constexpr (std::meta::is_const_type(std::meta::type_of(Var))) {
683 ns.addVariable(key, [:Var:]); // immutable: a value snapshot at load time
684 } else {
685 using VT = typename [:std::meta::type_of(Var):];
686 ns.addProperty(
687 key, +[]() -> VT { return [:Var:]; }, +[](VT v) { [:Var:] = v; });
688 }
689 }
690
692 static module_type add_submodule(module_type& m, const char* name) {
693 _open_namespace(m).beginNamespace(name); // create, then unwind
694 module_type sub{m};
695 sub.path.emplace_back(name);
696 return sub;
697 }
698
701};
702
703static_assert(::welder::rod<rod>,
704 "welder::rods::luabridge::rod must satisfy welder::rod");
705
706} // namespace welder::rods::luabridge
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
The C++-operator → Lua metamethod name map, shared by both Lua runtime rods (sol2 and LuaBridge3).
consteval auto aggregate_fields()
The fields an aggregate is initialized from: its non-static data members in declaration order (all pu...
consteval const char * lua_metamethod_name(std::meta::info f)
Map a member operator to its Lua metamethod __name, or nullptr if welder does not expose it (which al...
T make_object(A... args)
A factory that constructs T from the constructor arguments (works for normal constructors and,...
Definition rod.hpp:125
consteval const char * lua_metamethod_name(std::meta::info f)
Map a member operator to its Lua metamethod __name, or nullptr if welder does not expose it (which al...
void(A...) ctor_sig
The alias void(A...) — a LuaBridge3 constructor signature (a function type whose parameters are the c...
Definition rod.hpp:117
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
consteval void validate_return_policy()
Reject a return_policy on Fn (for language L) that contradicts Fn's return type.
Definition reflect.hpp:253
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
Splice-based accessors for data member Mem — the pointer-to-member-free route the rods bind a protect...
The class handle threaded from make_class to the add_* hooks: enough to re-open the class (its module...
Definition rod.hpp:165
std::string name
The class's Lua name.
Definition rod.hpp:168
module_scope mod
The enclosing module.
Definition rod.hpp:167
The enum handle threaded from make_enum to add_enumerator: the enclosing module, the enum's Lua name ...
Definition rod.hpp:179
A copyable handle to a welded module (or submodule) table: the borrowed Lua state plus the namespace ...
Definition rod.hpp:149
lua_State * L
The borrowed Lua state.
Definition rod.hpp:150
std::vector< std::string > path
Namespace segments under _G.
Definition rod.hpp:151
Per-module session — unused: LuaBridge3 registers namespace variables as live properties eagerly (no ...
Definition rod.hpp:159
static void _make_class(const module_scope &m, const char *name, std::index_sequence< I... >)
Create the class registration with its native (welded nearest-ancestor) bases, in one chained express...
Definition rod.hpp:332
static consteval auto _factory_array()
The make_object<T, A...> factory-function reflections (for the .new static function) as a fixed-size,...
Definition rod.hpp:301
static void add_operator(auto &h)
Bind member operator Fn under its Lua metamethod __name.
Definition rod.hpp:541
static void _add_static_function(Target &t, const char *name, std::index_sequence< I... >)
As _add_function, for a class's static methods (addStaticFunction).
Definition rod.hpp:361
static constexpr lang language
welder::lang::lua.
Definition rod.hpp:140
static module_type add_submodule(module_type &m, const char *name)
Create a submodule (nested namespace) named name under m.
Definition rod.hpp:692
static enum_handle make_enum(module_type &m, const char *name, const char *)
Create the enum's nested namespace (a Name = value table) on the module (doc ignored) and return a re...
Definition rod.hpp:578
static consteval auto _ctor_sigs_array()
The void(A...) constructor-signature reflections (for addConstructor) as a fixed-size,...
Definition rod.hpp:286
static void add_field(auto &h)
Bind data member Mem as a class property (read-only if const, else read/write).
Definition rod.hpp:467
module_scope module_type
A Lua module is a named table.
Definition rod.hpp:153
static constexpr bool _needs_registration
Whether LuaBridge3 can only convert T via runtime class registration.
Definition rod.hpp:211
static class_handle< T > make_nested_class(module_type &, auto &outer, const char *name, const char *, std::index_sequence< I... >)
Register a nested member type T under a temporary dotted module key ("Outer.Inner").
Definition rod.hpp:407
static enum_handle make_nested_enum(module_type &, auto &outer, const char *name, const char *)
Create the Name = value table for a nested member enum directly inside the enclosing class's table (m...
Definition rod.hpp:591
static consteval std::vector< std::vector< std::meta::info > > _ctor_arg_lists()
The set of constructor argument lists to expose for T, built from the pieces the DRIVER hands to add_...
Definition rod.hpp:264
enum_handle enum_handle_type
Definition rod.hpp:191
static void _push_module_table(const module_scope &m)
Push the module's namespace table onto the Lua stack (a raw walk from _G through the path segments); ...
Definition rod.hpp:249
typename std::remove_cvref_t< H >::type _class_type
The C++ type behind a class_handle<T>& (deduced from the driver's auto&).
Definition rod.hpp:217
static void add_static_method(auto &h)
Bind static-method overload group Fns as a class-table function (T.name(…)), grouped as in add_method...
Definition rod.hpp:508
static void add_function(module_type &m, const char *name=nullptr)
Bind free-function overload group Fns as one module-level function (a single variadic addFunction; na...
Definition rod.hpp:660
static void add_variable(module_type &m, session &, const char *name=nullptr)
Bind namespace variable Var onto the module.
Definition rod.hpp:677
static class_handle< T > make_class(module_type &m, const char *name, const char *, std::index_sequence< I... >)
Register class T (with its native bases and constructors) and return a re-openable handle.
Definition rod.hpp:390
static void _add_constructors(Cls &cls, std::index_sequence< I... >)
Register the whole constructor set on the (live) class cls: both the call form T(…) (addConstructor,...
Definition rod.hpp:322
static void add_method(auto &h)
Bind method overload group Fns as one method (obj:name(…)) via a single variadic addFunction — LuaBri...
Definition rod.hpp:496
static consteval const char * special_method_name(std::meta::info op_fn)
Map a member operator to its Lua metamethod name (nullptr = not exposed).
Definition rod.hpp:379
static void _add_function(Target &t, const char *name, std::index_sequence< I... >)
Register overload group Grp on target t (a live class or namespace) under name via LuaBridge3's varia...
Definition rod.hpp:348
static void close_module(module_type &, session &)
Close the session (no-op; see session).
Definition rod.hpp:700
static void finish_enum(auto &)
No whole-enum finalizer needed (unscoped export is done per-enumerator).
Definition rod.hpp:644
static constexpr bool has_native_caster
caster_oracle: T converts without welder registering a class iff LuaBridge3 does not classify it as n...
Definition rod.hpp:375
static void finish_nested_class(module_type &, auto &outer, auto &cls, const char *name)
Move the fully-registered nested class table onto the outer's class table (module....
Definition rod.hpp:424
class_handle< T > class_handle_type
The class / enum handles the per-class / per-enum hooks receive — exactly what make_class / make_enum...
Definition rod.hpp:190
static lb::Namespace _open_namespace(const module_scope &m)
Open the module's namespace chain from the global namespace, returning the innermost luabridge::Names...
Definition rod.hpp:224
static auto _open_class(const module_scope &m, const char *name)
Re-open class T under name in module m, returning the live luabridge::Namespace::Class<T> (created by...
Definition rod.hpp:237
static void set_module_doc(module_type &, const char *)
No runtime module docstring in Lua (its home is a generated stub).
Definition rod.hpp:653
static session open_module(module_type &)
Open a per-module session (unused; see session).
Definition rod.hpp:649
static void add_enumerator(enum_handle &e)
Add enumerator Enum (as its underlying integer) to the enum's table.
Definition rod.hpp:611
static void add_constructors(auto &h)
Register T's whole constructor set — exactly what LuaBridge3 wants (one variadic addConstructor for t...
Definition rod.hpp:447
welder's binding entry point: the welder::welder struct.