welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
bind_traits.hpp
Go to the documentation of this file.
1#pragma once
2#include <array>
3#include <cstddef>
4#include <meta>
5#include <string_view>
6#include <type_traits>
7#include <vector>
8
9#include <welder/diag.hpp> // the consteval diagnostics (stale_hook_signature)
10#include <welder/reflect.hpp> // resolution: welded_for / member_bound / public_bases
11
26
27namespace welder::inline v0::detail {
28
29// --- function / parameter introspection -------------------------------------
30
37template <std::meta::info Fn>
38consteval auto param_types() {
39 constexpr std::size_t n{std::meta::parameters_of(Fn).size()};
40 std::array<std::meta::info, n> types{};
41 std::size_t i{0};
42 for (auto p : std::meta::parameters_of(Fn))
43 types[i++] = std::meta::type_of(p);
44 return types;
45}
46
53template <std::meta::info Fn>
54consteval auto param_names() {
55 constexpr std::size_t n{std::meta::parameters_of(Fn).size()};
56 std::array<const char*, n> names{};
57 std::size_t i{0};
58 for (auto p : std::meta::parameters_of(Fn))
59 names[i++] = std::meta::has_identifier(p)
60 ? std::define_static_string(std::meta::identifier_of(p))
61 : nullptr;
62 return names;
63}
64
67 unsigned nurse;
68 unsigned patient;
69};
70
78template <std::meta::info Fn>
79consteval auto keep_alive_pairs() {
80 constexpr std::size_t n{
81 std::meta::annotations_of_with_type(Fn, ^^keep_alive_spec).size()};
82 std::array<keep_alive_pair, n> out{};
83 if constexpr (n != 0) {
84 std::size_t i{0};
85 for (auto a : std::meta::annotations_of_with_type(Fn, ^^keep_alive_spec)) {
86 auto s{std::meta::extract<keep_alive_spec>(a)};
87 out[i++] = {s.nurse, s.patient};
88 }
89 }
90 return out;
91}
92
100template <std::meta::info Fn>
101consteval bool all_params_named() {
102 for (auto p : std::meta::parameters_of(Fn))
103 if (!std::meta::has_identifier(p))
104 return false;
105 return true;
106}
107
108// --- constructor / method / operator eligibility ----------------------------
109
118consteval bool is_bindable_constructor(std::meta::info c) {
119 return std::meta::is_constructor(c) && std::meta::is_public(c) &&
120 !std::meta::is_deleted(c) && !std::meta::is_copy_constructor(c) &&
121 !std::meta::is_move_constructor(c) &&
122 !std::meta::parameters_of(c).empty();
123}
124
140consteval bool is_method_candidate(std::meta::info f) {
141 return std::meta::is_function(f) && !std::meta::is_constructor(f) &&
142 !std::meta::is_special_member_function(f) &&
143 !std::meta::is_destructor(f) && !std::meta::is_operator_function(f) &&
144 !std::meta::is_private(f) && !std::meta::is_deleted(f);
145}
146
158consteval bool is_operator_candidate(std::meta::info f) {
159 return std::meta::is_function(f) && std::meta::is_operator_function(f) &&
160 !std::meta::is_special_member_function(f) &&
161 !std::meta::is_private(f) && !std::meta::is_deleted(f);
162}
163
201template <class Resolution>
202consteval bool member_access_admitted(std::meta::info mem, lang L,
203 std::meta::info bound_into) {
204 if (std::meta::is_public(mem))
205 return true;
206 if (!std::meta::is_protected(mem))
207 return false; // private (or inaccessible): out by design, always
208 if constexpr (requires {
209 Resolution::protected_participates(mem, L, bound_into);
210 }) {
211 return Resolution::protected_participates(mem, L, bound_into);
212 } else if constexpr (requires {
213 Resolution::protected_participates(mem, L);
214 }) {
215 // A pre-bound_into hook: hard-error rather than silently ignore it.
216 throw ::welder::diag::stale_hook_signature{};
217 } else {
218 return ::welder::protected_welded(std::meta::parent_of(mem), L);
219 }
220}
221
241template <std::meta::info Mem>
244 using class_type = typename [:std::meta::parent_of(Mem):];
246 using field_type = typename [:std::meta::type_of(Mem):];
247
251 static const field_type& get(const class_type& c) { return c.[:Mem:]; }
254 static void set(class_type& c, const field_type& v) { c.[:Mem:] = v; }
255};
256
264consteval bool is_unary_operator(std::meta::info f) {
265 return std::meta::parameters_of(f).empty();
266}
267
268// --- aggregate initialization -----------------------------------------------
269//
270// An aggregate (a simple POD-like struct: no user-declared constructors) cannot
271// be constructed as T(a, b) — only brace-initialized T{a, b}. A backend may want
272// to synthesize a field constructor so the target language can build it with
273// values; these helpers decide when that is valid and expose the fields.
274
281template <class T>
282consteval auto aggregate_fields() {
283 constexpr auto ctx{std::meta::access_context::unchecked()};
284 constexpr std::size_t n{std::meta::nonstatic_data_members_of(^^T, ctx).size()};
285 std::array<std::meta::info, n> fs{};
286 // Guard the fill: std::array<T, 0>::operator[] is not usable (its size-0
287 // trap overload is not consteval, so merely instantiating it with the
288 // consteval-only std::meta::info is an error for a fieldless type).
289 if constexpr (n != 0) {
290 std::size_t i{0};
291 for (auto m : std::meta::nonstatic_data_members_of(^^T, ctx))
292 fs[i++] = m;
293 }
294 return fs;
295}
296
311template <class T, lang L, class Resolution>
312consteval bool aggregate_initializable() {
313 if (!std::is_aggregate_v<T> || !welder::public_bases(^^T).empty())
314 return false;
315 constexpr auto ctx{std::meta::access_context::unchecked()};
316 auto fields{std::meta::nonstatic_data_members_of(^^T, ctx)};
317 if (fields.empty())
318 return false;
319 const policy_kind pol{policy_of(^^T)};
320 for (auto m : fields)
321 if (!Resolution::class_member_participates(m, L, pol, ^^T))
322 return false;
323 return true;
324}
325
326// --- namespace-member eligibility -------------------------------------------
327
336consteval bool is_bindable_kind(std::meta::info mem) {
337 return (std::meta::is_type(mem) &&
338 (std::meta::is_class_type(mem) || std::meta::is_enum_type(mem))) ||
339 std::meta::is_function(mem) || std::meta::is_variable(mem);
340}
341
350consteval bool entity_bound(std::meta::info mem, lang L, policy_kind pol) {
351 return is_bindable_kind(mem) && welded_for(mem, L) && member_bound(mem, L, pol);
352}
353
364consteval bool namespace_has_bound(std::meta::info ns, lang L) {
365 constexpr auto ctx{std::meta::access_context::unchecked()};
366 const policy_kind pol{policy_of(ns)};
367 for (auto mem : std::meta::members_of(ns, ctx)) {
368 if (std::meta::is_namespace(mem)) {
369 if (member_bound(mem, L, pol) && namespace_has_bound(mem, L))
370 return true;
371 } else if (entity_bound(mem, L, pol)) {
372 return true;
373 }
374 }
375 return false;
376}
377
389consteval bool namespace_has_bindable(std::meta::info ns, lang L) {
390 constexpr auto ctx{std::meta::access_context::unchecked()};
391 const policy_kind pol{policy_of(ns)};
392 for (auto mem : std::meta::members_of(ns, ctx)) {
393 if (std::meta::is_namespace(mem)) {
394 if (member_bound(mem, L, pol) && namespace_has_bindable(mem, L))
395 return true;
396 } else if (is_bindable_kind(mem) && member_bound(mem, L, pol)) {
397 return true;
398 }
399 }
400 return false;
401}
402
403// --- overload groups (resolution-aware) ---------------------------------------
404//
405// Several target frameworks store ONE entity per name — a Lua table key, a
406// LuaCATS `function` declaration — so a name's C++ overloads must arrive as one
407// group. The CARRIAGE owns that grouping (it visits a group's first participating
408// overload, the *leader*, computes the whole set with these selectors and hands
409// it to the rod's group hook): the rods never re-derive membership, so a group is
410// exactly what the resolution admits — per-overload marks and bespoke
411// (signature-level) resolutions stay consistent across every rod, and a mixed
412// welded/unwelded set under one name can never split into clobbering halves.
413
422template <class Resolution>
423consteval std::vector<std::meta::info> method_overload_set(
424 std::meta::info fn, lang L, std::meta::info bound_into) {
425 const std::meta::info cls{std::meta::parent_of(fn)};
426 const policy_kind pol{::welder::policy_of(cls)};
427 const auto name{std::meta::identifier_of(fn)};
428 const bool is_static{std::meta::is_static_member(fn)};
429 std::vector<std::meta::info> out{};
430 for (auto m : std::meta::members_of(cls, std::meta::access_context::unchecked()))
431 if (is_method_candidate(m) &&
432 member_access_admitted<Resolution>(m, L, bound_into) &&
433 Resolution::class_member_participates(m, L, pol, bound_into) &&
434 std::meta::is_static_member(m) == is_static &&
435 std::meta::identifier_of(m) == name)
436 out.push_back(m);
437 return out;
438}
439
448template <class Resolution>
449consteval std::vector<std::meta::info> operator_overload_set(
450 std::meta::info fn, lang L, std::meta::info bound_into) {
451 const std::meta::info cls{std::meta::parent_of(fn)};
452 const policy_kind pol{::welder::policy_of(cls)};
453 std::vector<std::meta::info> out{};
454 for (auto m : std::meta::members_of(cls, std::meta::access_context::unchecked()))
455 if (is_operator_candidate(m) &&
456 member_access_admitted<Resolution>(m, L, bound_into) &&
457 Resolution::class_member_participates(m, L, pol, bound_into) &&
458 std::meta::operator_of(m) == std::meta::operator_of(fn) &&
460 out.push_back(m);
461 return out;
462}
463
473template <class Resolution>
474consteval std::vector<std::meta::info> function_overload_set(
475 std::meta::info fn, lang L, std::meta::info bound_into) {
476 const std::meta::info ns{std::meta::parent_of(fn)};
477 const policy_kind pol{::welder::policy_of(ns)};
478 const auto name{std::meta::identifier_of(fn)};
479 std::vector<std::meta::info> out{};
480 for (auto m : std::meta::members_of(ns, std::meta::access_context::unchecked()))
481 if (std::meta::is_function(m) &&
482 Resolution::member_participates(m, L, pol, bound_into) &&
483 std::meta::identifier_of(m) == name)
484 out.push_back(m);
485 return out;
486}
487
491 std::vector<std::meta::info> (*)(std::meta::info, lang, std::meta::info);
492
499template <overload_selector Select, std::meta::info Fn, lang L,
500 std::meta::info BoundInto>
501consteval auto overload_group() {
502 constexpr std::size_t n{Select(Fn, L, BoundInto).size()};
503 std::array<std::meta::info, n> out{};
504 // Guard the fill: std::array<T, 0>::operator[] is not usable (n is >= 1 for a
505 // group leader, but the guard keeps this well-formed regardless).
506 if constexpr (n != 0) {
507 auto v{Select(Fn, L, BoundInto)};
508 for (std::size_t i{0}; i < n; ++i)
509 out[i] = v[i];
510 }
511 return out;
512}
513
520template <overload_selector Select>
521consteval bool is_overload_leader(std::meta::info fn, lang L,
522 std::meta::info bound_into) {
523 auto v{Select(fn, L, bound_into)};
524 return !v.empty() && v.front() == fn;
525}
526
541template <class Resolution, std::meta::info Fn, lang L>
542consteval auto manual_function_group() {
543 if constexpr (!std::meta::has_identifier(Fn)) {
544 return std::array<std::meta::info, 1>{Fn};
545 } else {
546 constexpr std::size_t n{[] {
548 std::meta::parent_of(Fn))};
549 std::size_t extra{1};
550 for (auto m : v)
551 if (m == Fn)
552 extra = 0;
553 return v.size() + extra;
554 }()};
555 std::array<std::meta::info, n> out{};
556 out[0] = Fn;
557 std::size_t i{1};
558 for (auto m :
559 function_overload_set<Resolution>(Fn, L, std::meta::parent_of(Fn)))
560 if (m != Fn)
561 out[i++] = m;
562 return out;
563 }
564}
565
588template <class Resolution, std::meta::info Type, lang L, policy_kind Pol>
589consteval auto ctor_group() {
590 constexpr auto ctx{std::meta::access_context::unchecked()};
591 constexpr std::size_t n{[] {
592 std::size_t count{0};
593 for (auto c : std::meta::members_of(Type, ctx))
595 Resolution::class_member_participates(c, L, Pol, Type))
596 ++count;
597 return count;
598 }()};
599 std::array<std::meta::info, n> out{};
600 if constexpr (n != 0) {
601 std::size_t i{0};
602 for (auto c : std::meta::members_of(Type, ctx))
604 Resolution::class_member_participates(c, L, Pol, Type))
605 out[i++] = c;
606 }
607 return out;
608}
609
624template <class Resolution, std::meta::info Type, lang L>
625consteval bool default_ctor_admitted() {
626 constexpr auto ctx{std::meta::access_context::unchecked()};
627 for (auto c : std::meta::members_of(Type, ctx))
628 if (std::meta::is_constructor(c) && std::meta::parameters_of(c).empty())
629 return std::meta::is_public(c) && !std::meta::is_deleted(c) &&
630 Resolution::class_member_participates(
631 c, L, policy_kind::automatic, Type);
632 return true; // implicit (or absent): constructibility alone decides
633}
634
635// --- inheritance: native bases ----------------------------------------------
636//
637// `weld` marks a type as an independently-registered, module-discoverable entity,
638// NOT as an inheritance directive. The most-derived type's `weld` drives which
639// languages bind; a base need not be welded to contribute its members. Two kinds
640// of public base fall out for a binding:
641// * a welded base -> registered as its own target-language class, linked via
642// native inheritance (passed as a base of the derived class handle).
643// * a non-welded base -> a plain C++ mixin with no standalone type, whose
644// eligible members are flattened onto the derived binding.
645
656consteval void collect_native_bases(std::meta::info type, lang L,
657 std::vector<std::meta::info>& out) {
658 for (auto base : welder::public_bases(type)) {
659 if (welder::welded_for(base, L)) {
660 bool seen{false};
661 for (auto e : out)
662 if (e == base) {
663 seen = true;
664 break;
665 }
666 if (!seen)
667 out.push_back(base);
668 } else {
669 collect_native_bases(base, L, out); // descend through the mixin
670 }
671 }
672}
673
682template <std::meta::info Type, lang L>
683consteval auto native_base_types() {
684 constexpr std::size_t n{[] {
685 std::vector<std::meta::info> v;
686 collect_native_bases(Type, L, v);
687 return v.size();
688 }()};
689 std::array<std::meta::info, n> types{};
690 // Guard the fill: std::array<T, 0>::operator[] is not consteval, so it must
691 // not be instantiated when a type has no native bases (the common case).
692 if constexpr (n != 0) {
693 std::vector<std::meta::info> v;
694 collect_native_bases(Type, L, v);
695 std::size_t i{0};
696 for (auto base : v)
697 types[i++] = base;
698 }
699 return types;
700}
701
702} // namespace welder::detail
welder's consteval diagnostics, collected in one place: every hand-rolled compile-time error the libr...
The stored forms of the annotation vocabulary.
consteval bool is_overload_leader(std::meta::info fn, lang L, std::meta::info bound_into)
Whether fn is the first (declaration order) member of its Select overload set — the single visit on w...
consteval auto aggregate_fields()
The fields an aggregate is initialized from: its non-static data members in declaration order (all pu...
consteval std::vector< std::meta::info > method_overload_set(std::meta::info fn, lang L, std::meta::info bound_into)
The participating method overloads sharing fn's name and static-ness, from the class where fn is decl...
consteval bool is_operator_candidate(std::meta::info f)
The shape of a bindable member operator.
consteval bool namespace_has_bound(std::meta::info ns, lang L)
Whether ns holds anything that would bind, directly or nested.
consteval bool is_unary_operator(std::meta::info f)
Whether a member operator is unary (0 parameters) vs binary (1 parameter).
consteval auto ctor_group()
The participating constructors of Type under policy Pol: every bindable-shape constructor (see is_bin...
consteval bool entity_bound(std::meta::info mem, lang L, policy_kind pol)
Whether a leaf entity binds: a welded candidate that also resolves as bound.
consteval bool is_bindable_constructor(std::meta::info c)
A non-default, non-copy/move public constructor a backend should expose.
consteval auto manual_function_group()
The semi-manual (weld_function<Fn>) group: Fn first — the user's named entity resolves the group's ta...
std::vector< std::meta::info >(*)(std::meta::info, lang, std::meta::info) overload_selector
The signature of an overload-set selector specialization: the representative overload,...
consteval std::vector< std::meta::info > function_overload_set(std::meta::info fn, lang L, std::meta::info bound_into)
The participating free-function overloads sharing fn's name, from fn's declaring namespace,...
consteval bool is_method_candidate(std::meta::info f)
The shape of a bindable method: a plain non-private member function.
consteval auto overload_group()
Select's overload set for Fn as a fixed-size, splice-ready static array.
consteval auto param_types()
A function's parameter types, as a static array of reflections.
consteval bool default_ctor_admitted()
Whether the resolution admits Type's DEFAULT constructor.
consteval void collect_native_bases(std::meta::info type, lang L, std::vector< std::meta::info > &out)
Collect the native bases of type for L: its nearest welded ancestors.
consteval bool namespace_has_bindable(std::meta::info ns, lang L)
The greedy twin of namespace_has_bound: whether ns holds any bindable kind (ignoring the weld marker)...
consteval bool member_access_admitted(std::meta::info mem, lang L, std::meta::info bound_into)
Is mem's access level admitted for binding under Resolution?
consteval bool all_params_named()
Whether every parameter of Fn carries an identifier.
consteval std::vector< std::meta::info > operator_overload_set(std::meta::info fn, lang L, std::meta::info bound_into)
The participating operator overloads sharing fn's target slot (same operator and arity — hence the sa...
consteval auto param_names()
A function's parameter names, in order.
consteval bool aggregate_initializable()
Whether to synthesize an aggregate field constructor for T (language L, resolution Resolution).
consteval auto keep_alive_pairs()
The keep_alive dependencies declared on Fn, in declaration order.
consteval auto native_base_types()
The native bases of Type for L as a static array of type reflections.
consteval bool is_bindable_kind(std::meta::info mem)
The member kinds welder can expose from a namespace.
consteval bool welded_for(std::meta::info type, lang L)
Is type welded for language L — i.e.
Definition reflect.hpp:26
policy_kind
How greedily a type's members are reflected for binding.
@ automatic
Reflect every member unless explicitly excluded (default).
lang
The target languages welder ships rods for — but not the whole value space.
Definition lang.hpp:42
consteval policy_kind policy_of(std::meta::info type)
The reflection policy declared on type, defaulting to automatic.
Definition reflect.hpp:132
consteval bool member_bound(std::meta::info member, lang L, policy_kind pol)
The core decision a backend asks for each member: does member bind for language L under policy pol?
Definition reflect.hpp:276
consteval std::vector< std::meta::info > public_bases(std::meta::info type)
The types of the public base classes of type.
Definition reflect.hpp:307
Language-agnostic resolution: given a reflected type/member and a target language,...
Splice-based accessors for data member Mem — the pointer-to-member-free route the rods bind a protect...
static void set(class_type &c, const field_type &v)
Write the field (instantiated only for mutable fields).
typename[:std::meta::type_of(Mem):] field_type
The field's declared type (const included).
typename[:std::meta::parent_of(Mem):] class_type
The declaring class.
static const field_type & get(const class_type &c)
Read the field (a reference, so backends can apply their reference-internal return semantics,...
A keep_alive lifetime dependency: a (nurse, patient) index pair.
unsigned patient
The dependant kept alive until the nurse is collected.
unsigned nurse
The keeper whose collection bounds the dependency.
The stored form of a keep_alive annotation: a lifetime dependency between two of a call's entities,...