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 <cstdint>
5#include <meta>
6#include <sstream> // std::ostream (the stringifier-shape test) + stringify's stream
7#include <string>
8#include <string_view>
9#include <type_traits>
10#include <vector>
11
12#include <welder/diag.hpp> // the consteval diagnostics (stale_hook_signature)
13#include <welder/naming.hpp> // property naming: accessor_property_words / strip_accessor_word
14#include <welder/reflect.hpp> // resolution: welded_for / member_bound / public_bases
15
30
31namespace welder::inline v0::detail {
32
33// --- function / parameter introspection -------------------------------------
34
41template <std::meta::info Fn>
42consteval auto param_types() {
43 constexpr std::size_t n{std::meta::parameters_of(Fn).size()};
44 std::array<std::meta::info, n> types{};
45 std::size_t i{0};
46 for (auto p : std::meta::parameters_of(Fn))
47 types[i++] = std::meta::type_of(p);
48 return types;
49}
50
61template <std::meta::info Fn>
62consteval std::size_t trailing_default_count() {
63 auto ps = std::meta::parameters_of(Fn);
64 std::size_t n{0};
65 for (std::size_t i = ps.size(); i > 0; --i) {
66 if (!std::meta::has_default_argument(ps[i - 1]))
67 break;
68 ++n;
69 }
70 return n;
71}
72
79template <std::meta::info Fn>
80consteval auto param_names() {
81 constexpr std::size_t n{std::meta::parameters_of(Fn).size()};
82 std::array<const char*, n> names{};
83 std::size_t i{0};
84 for (auto p : std::meta::parameters_of(Fn))
85 names[i++] = std::meta::has_identifier(p)
86 ? std::define_static_string(std::meta::identifier_of(p))
87 : nullptr;
88 return names;
89}
90
93 unsigned nurse;
94 unsigned patient;
95};
96
104template <std::meta::info Fn>
105consteval auto keep_alive_pairs() {
106 constexpr std::size_t n{
107 std::meta::annotations_of_with_type(Fn, ^^keep_alive_spec).size()};
108 std::array<keep_alive_pair, n> out{};
109 if constexpr (n != 0) {
110 std::size_t i{0};
111 for (auto a : std::meta::annotations_of_with_type(Fn, ^^keep_alive_spec)) {
112 auto s{std::meta::extract<keep_alive_spec>(a)};
113 out[i++] = {s.nurse, s.patient};
114 }
115 }
116 return out;
117}
118
126template <std::meta::info Fn>
127consteval bool all_params_named() {
128 for (auto p : std::meta::parameters_of(Fn))
129 if (!std::meta::has_identifier(p))
130 return false;
131 return true;
132}
133
134// --- constructor / method / operator eligibility ----------------------------
135
147consteval bool is_bindable_constructor(std::meta::info c) {
148 return std::meta::is_constructor(c) && std::meta::is_public(c) &&
149 !std::meta::is_deleted(c) && !std::meta::is_copy_constructor(c) &&
150 !std::meta::is_move_constructor(c) &&
151 !std::meta::parameters_of(c).empty();
152}
153
169consteval bool is_method_candidate(std::meta::info f) {
170 return std::meta::is_function(f) && !std::meta::is_constructor(f) &&
171 !std::meta::is_special_member_function(f) &&
172 !std::meta::is_destructor(f) && !std::meta::is_operator_function(f) &&
173 !std::meta::is_private(f) && !std::meta::is_deleted(f);
174}
175
187consteval bool is_operator_candidate(std::meta::info f) {
188 return std::meta::is_function(f) && std::meta::is_operator_function(f) &&
189 !std::meta::is_special_member_function(f) &&
190 !std::meta::is_private(f) && !std::meta::is_deleted(f);
191}
192
193// --- freestanding (namespace-scope) operators --------------------------------
194//
195// A free operator is part of a type's interface exactly as a member one is (C++
196// finds it by ADL), so welder sweeps a welded type's enclosing namespace for
197// operators *anchored* on the type — one operand IS the type — and folds them
198// into the type's binding. Hidden friends are the one shape reflection cannot
199// see (P2996 enumerates neither a class's friends nor ADL): move them to
200// namespace scope, or bind them by hand on the returned class handle.
201
209consteval bool decays_to(std::meta::info t, std::meta::info type) {
210 return std::meta::dealias(std::meta::remove_cvref(t)) ==
211 std::meta::dealias(type);
212}
213
219consteval bool is_free_operator_candidate(std::meta::info f) {
220 return std::meta::is_function(f) && std::meta::is_operator_function(f) &&
221 !std::meta::is_class_member(f) && !std::meta::is_deleted(f);
222}
223
231consteval bool is_stringifier_for(std::meta::info f, std::meta::info type) {
233 std::meta::operator_of(f) != std::meta::operators::op_less_less)
234 return false;
235 auto ps{std::meta::parameters_of(f)};
236 if (ps.size() != 2)
237 return false;
238 const auto p0{std::meta::type_of(ps[0])};
239 return std::meta::is_lvalue_reference_type(p0) &&
240 decays_to(p0, ^^std::ostream) &&
241 decays_to(std::meta::type_of(ps[1]), type);
242}
243
251consteval bool free_operator_anchored(std::meta::info f, std::meta::info type) {
253 return false;
254 for (auto p : std::meta::parameters_of(f)) {
255 const auto t{std::meta::type_of(p)};
256 if (decays_to(t, type) && !std::meta::is_rvalue_reference_type(t))
257 return true;
258 }
259 return false;
260}
261
270consteval bool free_operator_reflected(std::meta::info f, std::meta::info type) {
271 if (std::meta::is_class_member(f))
272 return false;
273 auto ps{std::meta::parameters_of(f)};
274 return ps.size() == 2 && !decays_to(std::meta::type_of(ps[0]), type);
275}
276
286template <auto Fns, std::meta::info Type, bool Reflected>
287consteval auto partition_reflected() {
288 constexpr std::size_t n{[] {
289 std::size_t c{0};
290 for (auto f : Fns)
291 if (free_operator_reflected(f, Type) == Reflected)
292 ++c;
293 return c;
294 }()};
295 std::array<std::meta::info, n> out{};
296 // Guard the fill: std::array<T, 0>::operator[] is not consteval.
297 if constexpr (n != 0) {
298 std::size_t i{0};
299 for (auto f : Fns)
300 if (free_operator_reflected(f, Type) == Reflected)
301 out[i++] = f;
302 }
303 return out;
304}
305
311consteval std::meta::info enclosing_namespace(std::meta::info type) {
312 auto p{std::meta::parent_of(type)};
313 while (!std::meta::is_namespace(p))
314 p = std::meta::parent_of(p);
315 return p;
316}
317
324consteval std::meta::info comparison_operand(std::meta::info f,
325 std::meta::info type) {
326 for (auto p : std::meta::parameters_of(f))
327 if (!decays_to(std::meta::type_of(p), type))
328 return std::meta::type_of(p);
329 return type; // homogeneous: every operand is the type itself
330}
331
338template <auto Fns, std::meta::info Type>
340 for (auto f : Fns)
341 if (!decays_to(comparison_operand(f, Type), Type))
342 return true;
343 return false;
344}
345
357enum class cmp_slot : std::uint8_t { lt = 0, le = 1, gt = 2, ge = 3 };
358template <class A, class B, cmp_slot S>
363 static bool call(const A& a, const B& b) {
364 if constexpr (S == cmp_slot::lt)
365 return a < b;
366 else if constexpr (S == cmp_slot::le)
367 return a <= b;
368 else if constexpr (S == cmp_slot::gt)
369 return a > b;
370 else
371 return a >= b;
372 }
373};
374
383template <class T, std::meta::info Fn>
384std::string stringify(const T& self) {
385 std::ostringstream os{};
386 [:Fn:](os, self);
387 return os.str();
388}
389
427template <class Resolution>
428consteval bool member_access_admitted(std::meta::info mem, lang L,
429 std::meta::info bound_into) {
430 if (std::meta::is_public(mem))
431 return true;
432 if (!std::meta::is_protected(mem))
433 return false; // private (or inaccessible): out by design, always
434 if constexpr (requires {
435 Resolution::protected_participates(mem, L, bound_into);
436 }) {
437 return Resolution::protected_participates(mem, L, bound_into);
438 } else if constexpr (requires {
439 Resolution::protected_participates(mem, L);
440 }) {
441 // A pre-bound_into hook: hard-error rather than silently ignore it.
442 throw ::welder::diag::stale_hook_signature{};
443 } else {
444 return ::welder::protected_welded(std::meta::parent_of(mem), L);
445 }
446}
447
467template <std::meta::info Mem>
470 using class_type = typename [:std::meta::parent_of(Mem):];
472 using field_type = typename [:std::meta::type_of(Mem):];
473
477 static const field_type& get(const class_type& c) { return c.[:Mem:]; }
480 static void set(class_type& c, const field_type& v) { c.[:Mem:] = v; }
481};
482
493consteval bool is_unary_operator(std::meta::info f) {
494 const std::size_t n{std::meta::parameters_of(f).size()};
495 return (std::meta::is_class_member(f) && !std::meta::is_static_member(f))
496 ? n == 0
497 : n == 1;
498}
499
500// --- aggregate initialization -----------------------------------------------
501//
502// An aggregate (a simple POD-like struct: no user-declared constructors) cannot
503// be constructed as T(a, b) — only brace-initialized T{a, b}. A backend may want
504// to synthesize a field constructor so the target language can build it with
505// values; these helpers decide when that is valid and expose the fields.
506
513template <class T>
514consteval auto aggregate_fields() {
515 constexpr auto ctx{std::meta::access_context::unchecked()};
516 constexpr std::size_t n{std::meta::nonstatic_data_members_of(^^T, ctx).size()};
517 std::array<std::meta::info, n> fs{};
518 // Guard the fill: std::array<T, 0>::operator[] is not usable (its size-0
519 // trap overload is not consteval, so merely instantiating it with the
520 // consteval-only std::meta::info is an error for a fieldless type).
521 if constexpr (n != 0) {
522 std::size_t i{0};
523 for (auto m : std::meta::nonstatic_data_members_of(^^T, ctx))
524 fs[i++] = m;
525 }
526 return fs;
527}
528
543template <class T, lang L, class Resolution>
544consteval bool aggregate_initializable() {
545 if (!std::is_aggregate_v<T> || !welder::public_bases(^^T).empty())
546 return false;
547 constexpr auto ctx{std::meta::access_context::unchecked()};
548 auto fields{std::meta::nonstatic_data_members_of(^^T, ctx)};
549 if (fields.empty())
550 return false;
551 const policy_kind pol{policy_of(^^T)};
552 for (auto m : fields) {
553 // An UNNAMED field (an anonymous union, an unnamed bit-field) is
554 // structurally unbindable — the member sweep skips it — so it counts
555 // as non-participating here: synthesizing a field constructor would
556 // leak it as a positional parameter (aggregate init is positional and
557 // all-or-nothing).
558 if (!std::meta::has_identifier(m))
559 return false;
560 if (!Resolution::class_member_participates(m, L, pol, ^^T))
561 return false;
562 }
563 return true;
564}
565
581template <class T>
582consteval std::size_t aggregate_required_arity() {
583 constexpr auto fields{aggregate_fields<T>()};
584 std::size_t required{0};
585 // Guard the indexing: std::array<info, 0>::operator[] is not consteval, so
586 // it must not be instantiated for a fieldless type (same trap as the
587 // aggregate_fields fill).
588 if constexpr (fields.size() != 0) {
589 for (std::size_t i{0}; i < fields.size(); ++i)
590 if (!std::meta::has_default_member_initializer(fields[i]))
591 required = i + 1;
592 }
593 return required;
594}
595
611template <class T>
612consteval std::size_t aggregate_defaults_from() {
613 constexpr auto fields{aggregate_fields<T>()};
614 if (!std::meta::is_default_constructible_type(^^T))
615 return fields.size();
616 std::size_t from{aggregate_required_arity<T>()};
617 // Guarded indexing, as in aggregate_required_arity.
618 if constexpr (fields.size() != 0) {
619 for (std::size_t i{from}; i < fields.size(); ++i)
620 if (!std::meta::is_copy_constructible_type(
621 std::meta::type_of(fields[i])))
622 from = i + 1;
623 }
624 return from;
625}
626
627// --- namespace-member eligibility -------------------------------------------
628
637consteval bool is_bindable_kind(std::meta::info mem) {
638 return (std::meta::is_type(mem) &&
639 (std::meta::is_class_type(mem) || std::meta::is_enum_type(mem))) ||
640 std::meta::is_function(mem) || std::meta::is_variable(mem);
641}
642
651consteval bool entity_bound(std::meta::info mem, lang L, policy_kind pol) {
652 return is_bindable_kind(mem) && welded_for(mem, L) && member_bound(mem, L, pol);
653}
654
665consteval bool namespace_has_bound(std::meta::info ns, lang L) {
666 constexpr auto ctx{std::meta::access_context::unchecked()};
667 const policy_kind pol{policy_of(ns)};
668 for (auto mem : std::meta::members_of(ns, ctx)) {
669 if (std::meta::is_namespace(mem)) {
670 if (member_bound(mem, L, pol) && namespace_has_bound(mem, L))
671 return true;
672 } else if (entity_bound(mem, L, pol)) {
673 return true;
674 }
675 }
676 return false;
677}
678
690consteval bool namespace_has_bindable(std::meta::info ns, lang L) {
691 constexpr auto ctx{std::meta::access_context::unchecked()};
692 const policy_kind pol{policy_of(ns)};
693 for (auto mem : std::meta::members_of(ns, ctx)) {
694 if (std::meta::is_namespace(mem)) {
695 if (member_bound(mem, L, pol) && namespace_has_bindable(mem, L))
696 return true;
697 } else if (is_bindable_kind(mem) && member_bound(mem, L, pol)) {
698 return true;
699 }
700 }
701 return false;
702}
703
704// --- overload groups (resolution-aware) ---------------------------------------
705//
706// Several target frameworks store ONE entity per name — a Lua table key, a
707// LuaCATS `function` declaration — so a name's C++ overloads must arrive as one
708// group. The CARRIAGE owns that grouping (it visits a group's first participating
709// overload, the *leader*, computes the whole set with these selectors and hands
710// it to the rod's group hook): the rods never re-derive membership, so a group is
711// exactly what the resolution admits — per-overload marks and bespoke
712// (signature-level) resolutions stay consistent across every rod, and a mixed
713// welded/unwelded set under one name can never split into clobbering halves.
714
726template <class Resolution>
727consteval std::vector<std::meta::info> method_overload_set(
728 std::meta::info fn, lang L, std::meta::info bound_into) {
729 const std::meta::info cls{std::meta::parent_of(fn)};
730 const policy_kind pol{::welder::policy_of(cls)};
731 const auto name{std::meta::identifier_of(fn)};
732 const bool is_static{std::meta::is_static_member(fn)};
733 std::vector<std::meta::info> out{};
734 for (auto m : std::meta::members_of(cls, std::meta::access_context::unchecked()))
735 if (is_method_candidate(m) &&
736 member_access_admitted<Resolution>(m, L, bound_into) &&
737 Resolution::class_member_participates(m, L, pol, bound_into) &&
739 std::meta::is_static_member(m) == is_static &&
740 std::meta::identifier_of(m) == name)
741 out.push_back(m);
742 return out;
743}
744
756template <class Resolution>
757consteval void collect_member_operators(std::meta::info src, lang L,
758 std::meta::info bound_into,
759 std::vector<std::meta::info>& out) {
760 for (auto base : ::welder::public_bases(src))
761 if (!Resolution::is_native_base(base, L, src))
762 collect_member_operators<Resolution>(base, L, bound_into, out);
763 const policy_kind pol{::welder::policy_of(src)};
764 for (auto m :
765 std::meta::members_of(src, std::meta::access_context::unchecked()))
766 if (is_operator_candidate(m) &&
767 member_access_admitted<Resolution>(m, L, bound_into) &&
768 Resolution::class_member_participates(m, L, pol, bound_into))
769 out.push_back(m);
770}
771
785template <class Resolution>
786consteval std::vector<std::meta::info> operator_entries(std::meta::info type,
787 lang L) {
788 std::vector<std::meta::info> out{};
789 collect_member_operators<Resolution>(type, L, type, out);
790 const policy_kind pol{::welder::policy_of(type)};
791 for (auto m : std::meta::members_of(enclosing_namespace(type),
792 std::meta::access_context::unchecked()))
793 if (free_operator_anchored(m, type) &&
794 Resolution::class_member_participates(m, L, pol, type))
795 out.push_back(m);
796 return out;
797}
798
809template <class Resolution>
810consteval std::vector<std::meta::info> operator_slot_set(
811 std::meta::info fn, lang L, std::meta::info bound_into) {
812 std::vector<std::meta::info> out{};
813 for (auto m : operator_entries<Resolution>(bound_into, L))
814 if (std::meta::operator_of(m) == std::meta::operator_of(fn) &&
816 out.push_back(m);
817 return out;
818}
819
829template <class Resolution>
830consteval std::vector<std::meta::info> stringifier_entries(std::meta::info type,
831 lang L) {
832 std::vector<std::meta::info> out{};
833 const policy_kind pol{::welder::policy_of(type)};
834 for (auto m : std::meta::members_of(enclosing_namespace(type),
835 std::meta::access_context::unchecked()))
836 if (is_stringifier_for(m, type) &&
837 Resolution::class_member_participates(m, L, pol, type))
838 out.push_back(m);
839 return out;
840}
841
852template <class Resolution>
853consteval std::array<bool, 4> covered_comparison_slots(std::meta::info type,
854 lang L) {
855 std::array<bool, 4> covered{};
856 for (auto m : operator_entries<Resolution>(type, L)) {
857 if (is_unary_operator(m))
858 continue;
859 switch (std::meta::operator_of(m)) {
860 case std::meta::operators::op_less:
861 covered[0] = true;
862 break;
863 case std::meta::operators::op_less_equals:
864 covered[1] = true;
865 break;
866 case std::meta::operators::op_greater:
867 covered[2] = true;
868 break;
869 case std::meta::operators::op_greater_equals:
870 covered[3] = true;
871 break;
872 default:
873 break;
874 }
875 }
876 return covered;
877}
878
888template <class Resolution>
889consteval std::vector<std::meta::info> function_overload_set(
890 std::meta::info fn, lang L, std::meta::info bound_into) {
891 const std::meta::info ns{std::meta::parent_of(fn)};
892 const policy_kind pol{::welder::policy_of(ns)};
893 const auto name{std::meta::identifier_of(fn)};
894 std::vector<std::meta::info> out{};
895 for (auto m : std::meta::members_of(ns, std::meta::access_context::unchecked()))
896 if (std::meta::is_function(m) &&
897 Resolution::member_participates(m, L, pol, bound_into) &&
898 std::meta::identifier_of(m) == name)
899 out.push_back(m);
900 return out;
901}
902
906 std::vector<std::meta::info> (*)(std::meta::info, lang, std::meta::info);
907
914template <overload_selector Select, std::meta::info Fn, lang L,
915 std::meta::info BoundInto>
916consteval auto overload_group() {
917 constexpr std::size_t n{Select(Fn, L, BoundInto).size()};
918 std::array<std::meta::info, n> out{};
919 // Guard the fill: std::array<T, 0>::operator[] is not usable (n is >= 1 for a
920 // group leader, but the guard keeps this well-formed regardless).
921 if constexpr (n != 0) {
922 auto v{Select(Fn, L, BoundInto)};
923 for (std::size_t i{0}; i < n; ++i)
924 out[i] = v[i];
925 }
926 return out;
927}
928
935template <overload_selector Select>
936consteval bool is_overload_leader(std::meta::info fn, lang L,
937 std::meta::info bound_into) {
938 auto v{Select(fn, L, bound_into)};
939 return !v.empty() && v.front() == fn;
940}
941
956template <class Resolution, std::meta::info Fn, lang L>
957consteval auto manual_function_group() {
958 if constexpr (!std::meta::has_identifier(Fn)) {
959 return std::array<std::meta::info, 1>{Fn};
960 } else {
961 constexpr std::size_t n{[] {
963 std::meta::parent_of(Fn))};
964 std::size_t extra{1};
965 for (auto m : v)
966 if (m == Fn)
967 extra = 0;
968 return v.size() + extra;
969 }()};
970 std::array<std::meta::info, n> out{};
971 out[0] = Fn;
972 std::size_t i{1};
973 for (auto m :
974 function_overload_set<Resolution>(Fn, L, std::meta::parent_of(Fn)))
975 if (m != Fn)
976 out[i++] = m;
977 return out;
978 }
979}
980
1003template <class Resolution, std::meta::info Type, lang L, policy_kind Pol>
1004consteval auto ctor_group() {
1005 constexpr auto ctx{std::meta::access_context::unchecked()};
1006 constexpr std::size_t n{[] {
1007 std::size_t count{0};
1008 for (auto c : std::meta::members_of(Type, ctx))
1009 if (is_bindable_constructor(c) &&
1010 Resolution::class_member_participates(c, L, Pol, Type))
1011 ++count;
1012 return count;
1013 }()};
1014 std::array<std::meta::info, n> out{};
1015 if constexpr (n != 0) {
1016 std::size_t i{0};
1017 for (auto c : std::meta::members_of(Type, ctx))
1018 if (is_bindable_constructor(c) &&
1019 Resolution::class_member_participates(c, L, Pol, Type))
1020 out[i++] = c;
1021 }
1022 return out;
1023}
1024
1039template <class Resolution, std::meta::info Type, lang L>
1040consteval bool default_ctor_admitted() {
1041 constexpr auto ctx{std::meta::access_context::unchecked()};
1042 for (auto c : std::meta::members_of(Type, ctx))
1043 if (std::meta::is_constructor(c) && std::meta::parameters_of(c).empty())
1044 return std::meta::is_public(c) && !std::meta::is_deleted(c) &&
1045 Resolution::class_member_participates(
1046 c, L, policy_kind::automatic, Type);
1047 return true; // implicit (or absent): constructibility alone decides
1048}
1049
1071template <class Resolution, std::meta::info Type, lang L>
1072consteval bool copy_ctor_admitted() {
1073 constexpr auto ctx{std::meta::access_context::unchecked()};
1074 bool declared{false};
1075 for (auto c : std::meta::members_of(Type, ctx))
1076 if (std::meta::is_copy_constructor(c)) {
1077 declared = true;
1078 if (std::meta::is_public(c) && !std::meta::is_deleted(c) &&
1079 Resolution::class_member_participates(
1080 c, L, policy_kind::automatic, Type))
1081 return true;
1082 }
1083 return !declared; // implicit: constructibility alone decides
1084}
1085
1098template <std::meta::info Type>
1100 constexpr auto ctx{std::meta::access_context::unchecked()};
1101 for (auto c : std::meta::members_of(Type, ctx))
1102 if (std::meta::is_move_constructor(c) &&
1103 (!std::meta::annotations_of_with_type(c, ^^include_spec).empty() ||
1104 !std::meta::annotations_of_with_type(c, ^^only_spec).empty()))
1106}
1107
1108// --- method-backed properties (getter / setter marks) -------------------------
1109//
1110// A `[[=welder::getter]]` / `[[=welder::setter]]` member function binds as an
1111// idiomatic PROPERTY instead of a method, for the languages its mark covers.
1112// The machinery here mirrors the operator slot story: accessors are collected
1113// once per welded type across the flattening walk (own members + non-native
1114// bases'), validated, PAIRED by property name — the case-normalized word
1115// sequence of the explicit name or the get/set-stripped identifier, so the
1116// overload style (radius()/radius(double)), the prefix styles (get_x/set_x,
1117// getX/setX, GetX/SetX) and even mixed conventions all pair — and handed to the
1118// rod one resolved property at a time (add_property<T, Getter, Setter>). The
1119// method sweep skips accessor-marked functions for the covered languages (see
1120// method_overload_set / the carriage's bind_members); for languages a mark does
1121// not cover, the function binds as an ordinary method.
1122
1125 std::meta::info getter{};
1126 std::meta::info setter{};
1127};
1128
1133consteval bool carries_weld_as(std::meta::info m) {
1134 for (auto a : std::meta::annotations_of(m)) {
1135 auto t{std::meta::type_of(a)};
1136 if (std::meta::has_template_arguments(t) &&
1137 std::meta::template_of(t) == ^^weld_as_spec)
1138 return true;
1139 }
1140 return false;
1141}
1142
1150consteval std::vector<std::string> property_key(std::meta::info mem,
1151 accessor_role role, lang L) {
1152 std::string explicit_name{::welder::accessor_explicit_name(mem, role, L)};
1153 if (!explicit_name.empty())
1154 return naming::split_words(explicit_name);
1155 return naming::accessor_property_words(std::meta::identifier_of(mem));
1156}
1157
1167template <class Resolution>
1168consteval void collect_accessors(std::meta::info src, lang L,
1169 std::meta::info bound_into,
1170 std::vector<std::meta::info>& out) {
1171 for (auto base : ::welder::public_bases(src))
1172 if (!Resolution::is_native_base(base, L, src))
1173 collect_accessors<Resolution>(base, L, bound_into, out);
1174 const policy_kind pol{::welder::policy_of(src)};
1175 for (auto m :
1176 std::meta::members_of(src, std::meta::access_context::unchecked()))
1177 if (is_method_candidate(m) &&
1178 member_access_admitted<Resolution>(m, L, bound_into) &&
1179 Resolution::class_member_participates(m, L, pol, bound_into) &&
1181 out.push_back(m);
1182}
1183
1197template <class Resolution>
1199 std::meta::info src, lang L, std::meta::info bound_into,
1200 const std::vector<std::vector<std::string>>& keys) {
1201 for (auto base : ::welder::public_bases(src))
1202 if (!Resolution::is_native_base(base, L, src))
1203 validate_property_shadowing<Resolution>(base, L, bound_into, keys);
1204 constexpr auto ctx{std::meta::access_context::unchecked()};
1205 const policy_kind pol{::welder::policy_of(src)};
1206 auto clashes{[&](std::meta::info m) {
1207 const auto words{naming::split_words(std::meta::identifier_of(m))};
1208 for (const auto& k : keys)
1209 if (k == words)
1210 return true;
1211 return false;
1212 }};
1213 for (auto m : std::meta::nonstatic_data_members_of(src, ctx))
1214 if (std::meta::has_identifier(m) &&
1215 member_access_admitted<Resolution>(m, L, bound_into) &&
1216 Resolution::class_member_participates(m, L, pol, bound_into) &&
1217 clashes(m))
1219 for (auto m : std::meta::members_of(src, ctx))
1220 if (is_method_candidate(m) &&
1221 member_access_admitted<Resolution>(m, L, bound_into) &&
1222 Resolution::class_member_participates(m, L, pol, bound_into) &&
1223 !::welder::is_accessor_for(m, L) && clashes(m))
1225}
1226
1244template <class Resolution>
1245consteval std::vector<property_entry> property_entries(std::meta::info type,
1246 lang L) {
1247 std::vector<std::meta::info> acc{};
1248 collect_accessors<Resolution>(type, L, type, acc);
1249
1250 for (auto m : acc) {
1254 if (std::meta::is_static_member(m))
1256 if (std::meta::is_virtual(m))
1258 if (carries_weld_as(m))
1260 }
1261
1262 std::vector<property_entry> props{};
1263 std::vector<std::vector<std::string>> keys{};
1264 for (auto m : acc) {
1266 continue;
1267 if (!std::meta::parameters_of(m).empty() ||
1268 std::meta::dealias(std::meta::return_type_of(m)) == ^^void ||
1269 !std::meta::is_const(m) ||
1270 std::meta::is_rvalue_reference_qualified(m))
1271 throw diag::malformed_getter{};
1272 auto key{property_key(m, accessor_role::getter, L)};
1273 for (const auto& k : keys)
1274 if (k == key)
1276 props.push_back({m, std::meta::info{}});
1277 keys.push_back(std::move(key));
1278 }
1279 for (auto m : acc) {
1281 continue;
1282 if (std::meta::parameters_of(m).size() != 1 ||
1283 std::meta::is_rvalue_reference_qualified(m))
1284 throw diag::malformed_setter{};
1285 const auto key{property_key(m, accessor_role::setter, L)};
1286 bool matched{false};
1287 for (std::size_t i{0}; i < keys.size(); ++i) {
1288 if (keys[i] != key)
1289 continue;
1290 if (props[i].setter != std::meta::info{})
1292 props[i].setter = m;
1293 matched = true;
1294 break;
1295 }
1296 if (!matched)
1298 }
1299
1300 validate_property_shadowing<Resolution>(type, L, type, keys);
1301 return props;
1302}
1303
1315template <std::meta::info Getter, lang L, class Style>
1316consteval const char* property_bound_name() {
1317 constexpr std::string_view explicit_name{std::define_static_string(
1319 if constexpr (!explicit_name.empty())
1320 return explicit_name.data();
1321 else
1322 return std::define_static_string(
1323 naming::strip_accessor_word(Style::transform_field(Getter)));
1324}
1325
1326// --- inheritance: native bases ----------------------------------------------
1327//
1328// `weld` marks a type as an independently-registered, module-discoverable entity,
1329// NOT as an inheritance directive. The most-derived type's `weld` drives which
1330// languages bind; a base need not be welded to contribute its members. Two kinds
1331// of public base fall out for a binding:
1332// * a welded base -> registered as its own target-language class, linked via
1333// native inheritance (passed as a base of the derived class handle).
1334// * a non-welded base -> a plain C++ mixin with no standalone type, whose
1335// eligible members are flattened onto the derived binding.
1336
1347consteval void collect_native_bases(std::meta::info type, lang L,
1348 std::vector<std::meta::info>& out) {
1349 for (auto base : welder::public_bases(type)) {
1350 if (welder::welded_for(base, L)) {
1351 bool seen{false};
1352 for (auto e : out)
1353 if (e == base) {
1354 seen = true;
1355 break;
1356 }
1357 if (!seen)
1358 out.push_back(base);
1359 } else {
1360 collect_native_bases(base, L, out); // descend through the mixin
1361 }
1362 }
1363}
1364
1373template <std::meta::info Type, lang L>
1374consteval auto native_base_types() {
1375 constexpr std::size_t n{[] {
1376 std::vector<std::meta::info> v;
1377 collect_native_bases(Type, L, v);
1378 return v.size();
1379 }()};
1380 std::array<std::meta::info, n> types{};
1381 // Guard the fill: std::array<T, 0>::operator[] is not consteval, so it must
1382 // not be instantiated when a type has no native bases (the common case).
1383 if constexpr (n != 0) {
1384 std::vector<std::meta::info> v;
1385 collect_native_bases(Type, L, v);
1386 std::size_t i{0};
1387 for (auto base : v)
1388 types[i++] = base;
1389 }
1390 return types;
1391}
1392
1393} // 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 std::meta::info enclosing_namespace(std::meta::info type)
The nearest enclosing namespace of type — the scope its anchored free operators are swept from (for a...
consteval auto partition_reflected()
Split slot group Fns by free_operator_reflected — the entries whose reflectedness equals Reflected,...
consteval std::vector< property_entry > property_entries(std::meta::info type, lang L)
The resolved properties of type for language L: every participating accessor collected (own + flatten...
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 std::size_t aggregate_required_arity()
How many leading fields the synthesized aggregate field constructor REQUIRES: everything up to and in...
consteval std::vector< std::meta::info > operator_entries(std::meta::info type, lang L)
Every participating operator entry of type's binding — member operators (own + flattened bases') and ...
consteval void validate_property_shadowing(std::meta::info src, lang L, std::meta::info bound_into, const std::vector< std::vector< std::string > > &keys)
Guard the property surface against name shadowing: no property's key may equal a bound data member's ...
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 an operator function is unary vs binary.
consteval auto ctor_group()
The participating constructors of Type under policy Pol: every bindable-shape constructor (see is_bin...
consteval std::vector< std::meta::info > stringifier_entries(std::meta::info type, lang L)
The participating stringifier entries for type: free operator<<(std::ostream&, T) overloads of its en...
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 std::meta::info comparison_operand(std::meta::info f, std::meta::info type)
The operand a comparison synthesized from spaceship overload f takes: the parameter type that is not ...
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::size_t trailing_default_count()
How many TRAILING parameters of Fn carry a C++ default argument.
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 bool copy_ctor_admitted()
Whether the resolution admits Type's copy constructor — the marks half of the copy decision; the carr...
consteval bool has_heterogeneous_comparison()
Whether spaceship group Fns contains a heterogeneous overload (an operand that is not Type itself) — ...
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 bool free_operator_reflected(std::meta::info f, std::meta::info type)
Whether anchored free operator f binds reflected for type: type is the right operand and the left is ...
consteval void collect_accessors(std::meta::info src, lang L, std::meta::info bound_into, std::vector< std::meta::info > &out)
Collect the participating accessor-marked member functions visible on bound_into's binding: src's own...
consteval std::vector< std::string > property_key(std::meta::info mem, accessor_role role, lang L)
The pairing key of accessor mem's property for language L: the case-normalized word sequence of its e...
consteval std::array< bool, 4 > covered_comparison_slots(std::meta::info type, lang L)
Which relational slots (lt, le, gt, ge — indexed by cmp_slot) are already covered by an explicit part...
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 std::size_t aggregate_defaults_from()
The field index from which a value-extracting backend (the Python rods) can attach the NSDMI defaults...
consteval bool free_operator_anchored(std::meta::info f, std::meta::info type)
Whether free operator f is anchored on type: some parameter decays to exactly type (an rvalue-referen...
consteval bool is_free_operator_candidate(std::meta::info f)
The shape of a bindable freestanding operator: a non-deleted namespace-scope operator function.
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 std::vector< std::meta::info > operator_slot_set(std::meta::info fn, lang L, std::meta::info bound_into)
The participating operator entries sharing fn's target slot (same operator and arity — hence the same...
consteval const char * property_bound_name()
The bound name of the property Getter defines, for language L under name style Style: the mark's expl...
consteval bool all_params_named()
Whether every parameter of Fn carries an identifier.
consteval bool is_stringifier_for(std::meta::info f, std::meta::info type)
Whether f is the stream-inserter ("stringifier") shape for type: operator<<(std::ostream&,...
std::string stringify(const T &self)
The stringifier wrapper every runtime rod binds for a swept ostream inserter (see is_stringifier_for)...
consteval void collect_member_operators(std::meta::info src, lang L, std::meta::info bound_into, std::vector< std::meta::info > &out)
Collect the participating member operators visible on bound_into's binding: src's own,...
consteval bool decays_to(std::meta::info t, std::meta::info type)
Whether type reflection t is exactly the (welded) type type once stripped of cv/ref qualifiers and al...
consteval void validate_move_ctor_marks()
Reject an include/only mark on a move constructor of Type.
consteval auto param_names()
A function's parameter names, in order.
consteval bool carries_weld_as(std::meta::info m)
Does m carry a (templated) weld_as annotation?
cmp_slot
The rewritten-expression comparison a rod binds for a type whose C++ comparisons come from operator<=...
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 std::string strip_accessor_word(std::string_view styled)
Strip the accessor word off an already styled accessor name, re-joining the remainder in styled's own...
Definition naming.hpp:237
consteval std::vector< std::string > accessor_property_words(std::string_view id)
The words of an accessor's property: id split, with a leading get / set word stripped when at least o...
Definition naming.hpp:218
consteval std::vector< std::string > split_words(std::string_view id)
Split an identifier into lower-cased words, however it was spelled.
Definition naming.hpp:63
consteval bool welded_for(std::meta::info type, lang L)
Is type welded for language L — i.e.
Definition reflect.hpp:40
consteval bool is_accessor_for(std::meta::info member, lang L)
Does member supply either property half for language L — i.e.
Definition reflect.hpp:290
consteval bool accessor_marked(std::meta::info member, accessor_role role, lang L)
Does member carry a getter/setter mark of role covering L?
Definition reflect.hpp:276
policy_kind
How greedily a type's members are reflected for binding.
@ automatic
Reflect every member unless explicitly excluded (default).
constexpr detail::accessor_spec setter
Mark a member function as a property setter (exactly one parameter) — the write half of welder::gette...
lang
The target languages welder ships rods for — but not the whole value space.
Definition lang.hpp:42
accessor_role
Which half of a property a marked accessor function supplies.
@ getter
The function reads the property's value (const, no parameters).
@ setter
The function writes it (exactly one parameter).
consteval policy_kind policy_of(std::meta::info type)
The reflection policy declared on type, defaulting to automatic.
Definition reflect.hpp:148
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:387
consteval std::string accessor_explicit_name(std::meta::info member, accessor_role role, lang L)
The explicit property name an accessor mark of role forces for L, or "" when the name derives from th...
Definition reflect.hpp:314
consteval std::vector< std::meta::info > public_bases(std::meta::info type)
The types of the public base classes of type.
Definition reflect.hpp:418
Language-agnostic name styling: reshape a C++ identifier into a target language's naming convention,...
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,...
The stored form of an include mark: the languages a member is opted into.
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,...
The stored form of an only mark: the complete set of languages a member may bind for — implicitly,...
One resolved property: its getter and its (optional) setter.
std::meta::info getter
The read half (always present).
std::meta::info setter
The write half; a null reflection = read-only.
static bool call(const A &a, const B &b)
Evaluate a OP b through C++'s rewriting rules.
The stored form of a weld_as annotation: a forced target-language name.
Thrown by property_entries (<welder/bind_traits.hpp>) when one function carries both a getter and a s...
Definition diag.hpp:137
Thrown by property_entries (<welder/bind_traits.hpp>) when an accessor-marked function also carries a...
Definition diag.hpp:173
Thrown by property_entries (<welder/bind_traits.hpp>) when two getters (or two setters) resolve to th...
Definition diag.hpp:148
Thrown by property_entries (<welder/bind_traits.hpp>) when a [[=welder::getter]] sits on a function t...
Definition diag.hpp:115
Thrown by property_entries (<welder/bind_traits.hpp>) when a [[=welder::setter]] sits on a function t...
Definition diag.hpp:126
Thrown by validate_move_ctor_marks (<welder/bind_traits.hpp>) — the carriage runs it for every welded...
Definition diag.hpp:91
Thrown by property_entries (<welder/bind_traits.hpp>) when a resolved property name collides with a b...
Definition diag.hpp:212
Thrown by property_entries (<welder/bind_traits.hpp>) when a participating setter's property has no p...
Definition diag.hpp:160
Thrown by property_entries (<welder/bind_traits.hpp>) on an accessor-marked static member function: s...
Definition diag.hpp:185
Thrown by property_entries (<welder/bind_traits.hpp>) on an accessor-marked virtual member function: ...
Definition diag.hpp:198