welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
welder::rod Concept Reference

The contract a rod (a welder backend, welder::rods::…::rod) must satisfy to plug into the generic driver. More...

#include <welder/concepts.hpp>

Concept definition

template<class B>
concept rod =
requires {
{ B::language } -> std::convertible_to<lang>;
typename B::module_type;
} &&
requires(typename B::module_type& m, const char* s,
std::remove_cvref_t<decltype(B::open_module(
std::declval<typename B::module_type&>()))> session) {
{ B::open_module(m) };
{ B::add_submodule(m, s) } -> std::same_as<typename B::module_type>;
B::set_module_doc(m, s);
B::close_module(m, session);
{ B::special_method_name(^^int) } -> std::convertible_to<const char*>;
B::template add_function<std::array<std::meta::info, 1>{^^int},
B::template add_variable<^^int, detail::any_type>(m, session);
} &&
requires(typename B::template class_handle_type<detail::any_type>& cls,
typename B::template enum_handle_type<detail::any_enum>& en,
const char* s) {
B::template add_constructors<detail::any_type,
std::array<std::meta::info, 0>{}, false,
false, false>(cls);
B::template add_field<^^int, detail::any_type>(cls);
B::template add_property<detail::any_type, ^^int, std::meta::info{}>(cls,
s);
B::template add_method<std::array<std::meta::info, 1>{^^int},
B::template add_static_method<std::array<std::meta::info, 1>{^^int},
B::template add_operator<detail::any_type,
std::array<std::meta::info, 1>{^^int}>(cls);
B::template add_comparisons<detail::any_type,
std::array<std::meta::info, 1>{^^int},
std::array<bool, 4>{}>(cls);
B::template add_stringifier<detail::any_type, ^^int>(cls);
B::template add_enumerator<^^int, detail::any_type>(en);
B::template finish_enum<detail::any_enum>(en);
}
The one bindability fact a backend must provide: can it natively convert a type without welder regist...
Definition concepts.hpp:87
The contract a rod (a welder backend, welder::rods::…::rod) must satisfy to plug into the generic dri...
Definition concepts.hpp:263
A placeholder class type for shape-probing a member template inside a concept.
Definition concepts.hpp:51

Detailed Description

The contract a rod (a welder backend, welder::rods::…::rod) must satisfy to plug into the generic driver.

A rod B is a stateless struct; nothing is inherited, and every member is static. The concept shape-checks almost the whole contract — the associated types, the module/session machinery, and every emission hook — so a rod that omits or mis-signs one fails here with a clear "not a `welder::rod`" rather than a deep error the first time the driver instantiates it. The templated hooks can't be quantified over every type/reflection, so — as welder::caster_oracle probes has_native_caster — they are probed once with fixed placeholders (welder::detail::any_type, welder::detail::any_enum, ^^int), checking shape (the hook exists with a compatible signature), not per-type correctness. The per-class / per-enum hooks are probed against the rod's own declared handle types (class_handle_type / enum_handle_type); every emission hook returns void, so only signatures are substituted and no hook body is instantiated.

Note
The one gap: the factories themselves. make_class and make_enum return auto, so merely naming them in a requirement would force their bodies to be instantiated to deduce the return type — and a rod's make_class legitimately does real work there (the sol2 rod registers a type's constructors inside it), which a placeholder type does not survive. So the two factories stay contract-by-documentation**, enforced when the driver instantiates the rod over a real welded type. The concept sidesteps them by requiring the handle types as associated aliases* (class_handle_type<T> / enum_handle_type<E> — what the factories yield) rather than deducing them from a factory call, which is what lets the per-handle hooks be checked without instantiating any factory body.

Associated:**

static constexpr lang language; // the target language B binds to
using module_type = ...; // B's module handle (passed by ref)
template <class T> static constexpr bool has_native_caster; // caster_oracle
template <class T> using class_handle_type = ...; // what make_class yields for T
template <class E> using enum_handle_type = ...; // what make_enum yields for E
lang
The target languages welder ships rods for — but not the whole value space.
Definition lang.hpp:42

Type binding** (the class handle is class_handle_type<T>, what make_class returns). The per-member hooks take a trailing class Style (a welder::naming::name_style) so the rod resolves its own name via welder::name_of<Mem, language, Style, ent_kind::…>() — which also applies any [[=welder::weld_as]] override. The class name is styled by the driver and passed to make_class ready-made.

Callables arrive as whole overload groups — an auto Fns non-type argument, a std::array<std::meta::info, N> (N ≥ 1, declaration order) of overloads that share one target name. The CARRIAGE computes the group from its resolution and gates every member; the rod only emits — a chained-def framework (pybind11) loops the group, a one-value-per-name framework (the Lua rods) registers it as one overload set. The group's name resolves from Fns[0]. Constructors arrive the same way, as one add_constructors call carrying the participating constructor reflections plus three carriage-computed flags: the two synthesized forms (the default constructor — decided against construction_type<T> where the rod nominates one — and the aggregate field constructor) and the admitted copy constructor (Copyable — never an init overload; a rod gives it the target language's own copy spelling, or ignores it where none exists):

template <class T, auto Bases, std::size_t... I>
static auto make_class(module_type&, const char* name, const char* doc,
std::index_sequence<I...>); // Bases[I] spliced
// OPTIONAL (requires-detected): place a NESTED member type under its
// enclosing type's binding. The carriage prefers these for a class-scoped
// type (the outer class handle is the registration scope — Python:
// module.Outer.Inner); a rod without them falls back to the module-scope
// factories above, i.e. flat placement under the type's resolved name.
template <class T, auto Bases, std::size_t... I>
static auto make_nested_class(module_type&, auto& outer_cls, const char* name,
const char* doc, std::index_sequence<I...>);
// OPTIONAL, preferred over the plain form when declared: the
// declaring-entity-aware nested factory. Decl is ^^T, or the MEMBER ALIAS
// an (unnameable) template specialization was registered through — the one
// C++-spellable name such a target has, which a text-emitting rod needs.
template <class T, std::meta::info Decl, auto Bases, std::size_t... I>
static auto make_nested_class(module_type&, auto& outer_cls, const char* name,
const char* doc, std::index_sequence<I...>);
template <class E>
static auto make_nested_enum(module_type&, auto& outer_cls, const char* name,
const detail::enum_doc& doc); // or a `const char*`
// summary — the same dual form as make_enum below
// OPTIONAL: called after a nested class's whole interior has registered
// (innermost-first). For a rod whose class handle re-opens the class by
// name/path (LuaBridge3), this is where the class table actually moves
// under the outer — moving it at creation would break the re-opens.
template <class T>
static void finish_nested_class(module_type&, auto& outer_cls, auto& cls,
const char* name);
// OPTIONAL (welder::two_phase_rod): retrieve an ALREADY-registered class as a
// fillable handle. Declaring it opts the rod into the driver's two-phase
// namespace sweep — register every type NAME, bind the opaque containers that
// use them, then fill members — so a container-typed member/signature never
// spells a raw C++ name in a stub. class_handle_type<T> is the return; the
// nested form retrieves from the enclosing class handle.
template <class T>
static class_handle_type<T> reopen_class(module_type& scope, const char* name);
template <class T>
static class_handle_type<T> reopen_nested_class(module_type&, auto& outer,
const char* name);
template <class T, auto Ctors, bool HasDefault, bool Aggregate, bool Copyable>
static void add_constructors(auto& cls); // the whole participating set;
// Copyable = the admitted copy constructor (never an init overload —
// the Python rods emit __copy__/__deepcopy__, the Lua rods ignore it)
template <std::meta::info Mem, class Style> static void add_field(auto& cls);
template <class T, std::meta::info Getter, std::meta::info Setter>
static void add_property(auto& cls, const char* name);
// one resolved method-backed property (getter/setter marks): Getter is
// the read half, Setter the write half or a null reflection (= read-
// only). The NAME arrives resolved (the driver owns property naming —
// the mark's explicit name verbatim, else the styled-then-stripped
// identifier); the doc rides the getter's [[=welder::doc]]
template <auto Fns, class Style> static void add_method(auto& cls);
template <auto Fns, class Style> static void add_static_method(auto& cls);
template <class T, auto Fns> static void add_operator(auto& cls);
// one (operator, arity) SLOT, whole: member overloads (own + flattened
// bases') and anchored FREE operators mixed — a free entry with T on the
// right binds reflected (__radd__ &co.) on the Python rods
template <class T, auto Fns, auto Covered>
static void add_comparisons(auto& cls);
// the operator<=> group; synthesize the relational slots not already
// Covered ({lt,le,gt,ge} flags) via rewritten expressions
template <class T, std::meta::info Fn>
static void add_stringifier(auto& cls);
// the free ostream inserter -> __str__ / __tostring
static consteval const char* special_method_name(std::meta::info op_fn);
// target special-method name for an operator (member or free), or
// nullptr if the backend does not expose it (drives add_operator
// eligibility)
consteval detail::doc_spec< N > doc(const char(&s)[N])
Attach a docstring to a namespace, class, function, or function parameter.
The raw documentation pieces of an enum, handed to a style to assemble.
Definition doc.hpp:277

Enum binding** (the enum handle is whatever make_enum returns; deduced). make_enum takes welder::detail::enum_doc — the enum summary plus its documented enumerators — when the backend folds enumerator docs into the class docstring (the Python rods, whose DocStyle::format_enum renders an Attributes section); a backend that does not (the Lua/luacats rods) declares the legacy summary-only const char* doc form, and the carriage's make_enum_of picks whichever is present:

template <class E> static auto make_enum(module_type&, const char* name,
// legacy alternative: (module_type&, const char* name, const char* summary)
template <std::meta::info Enum, class Style> static void add_enumerator(auto& e);
template <class E> static void finish_enum(auto& e); // e.g. export unscoped

Namespace / module binding** (a "session" is backend scratch state — e.g. an accumulator for deferred, batched attributes — obtained per (sub)module):

static auto open_module(module_type&); // -> session
static void set_module_doc(module_type&, const char* doc);
template <auto Fns, class Style> static void add_function(module_type&, const char* name = nullptr);
// a free-function overload group (array NTTP, like add_method)
template <std::meta::info Var, class Style> static void add_variable(module_type&, session&, const char* name = nullptr);
static module_type add_submodule(module_type&, const char* name); // name pre-styled
static void close_module(module_type&, session&); // finalize the session
Template Parameters
Bthe candidate rod type.

Definition at line 263 of file concepts.hpp.