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
27#include <array>
28#include <cstddef>
29#include <cstdint>
30#include <memory>
31#include <meta>
32#include <optional> // lazy NSDMI defaults (Optional[F] = None; caster: <pybind11/stl.h>)
33#include <string>
34#include <type_traits>
35#include <utility>
36
37#include <welder/welder.hpp> // welder::welder + the rod contract + driver
38#include <welder/rods/python/doc_style.hpp> // welder::rods::python::google_style
39#include <welder/rods/python/operators.hpp> // welder::rods::python::operator_dunder
40#include <welder/rods/python/trampoline.hpp> // trampoline_for / gate / coverage
41#include <welder/bind_traits.hpp>// param_types / param_names / aggregate_fields
42#include <welder/doc.hpp> // function_docstring
43
44#include <pybind11/pybind11.h>
45#include <pybind11/native_enum.h> // py::native_enum (stdlib enum binding)
46#include <pybind11/stl_bind.h> // py::bind_vector / py::bind_map (opaque containers)
47
48#include <welder/containers.hpp> // container_kind_of (bind_vector vs bind_map)
49#include <welder/rods/python/array_interface.hpp> // numpy __array_interface__ for POD vectors
50
57#ifndef WELDER_OPAQUE
58#define WELDER_OPAQUE(...) PYBIND11_MAKE_OPAQUE(__VA_ARGS__)
59#endif
60
61namespace welder::inline v0::rods::pybind11 {
62
63// Inside `welder::rods::pybind11`, the unqualified name `pybind11` resolves to *this*
64// namespace, not the library. Alias the real one once — the way pybind11's own
65// docs spell it — and use `py::` for every library reference below.
66namespace py = ::pybind11;
67
86template <::welder::doc_style DocStyle = ::welder::rods::python::google_style>
87struct rod {
88 static constexpr lang language{lang::py};
89 using module_type = py::module_;
90
91 protected:
92 // --- implementation helpers (not part of the welder::rod contract) --
93
133 template <class T>
134 static constexpr bool _needs_registration =
135 std::is_enum_v<std::remove_cvref_t<T>> ||
136 std::is_base_of_v<py::detail::type_caster_base<py::detail::intrinsic_t<T>>,
137 py::detail::make_caster<T>>;
138
147 static consteval py::return_value_policy _return_value_policy(::welder::rv_kind k) {
148 switch (k) {
149 case ::welder::rv_kind::automatic: return py::return_value_policy::automatic;
150 case ::welder::rv_kind::automatic_reference: return py::return_value_policy::automatic_reference;
151 case ::welder::rv_kind::take_ownership: return py::return_value_policy::take_ownership;
152 case ::welder::rv_kind::copy: return py::return_value_policy::copy;
153 case ::welder::rv_kind::move: return py::return_value_policy::move;
154 case ::welder::rv_kind::reference: return py::return_value_policy::reference;
155 case ::welder::rv_kind::reference_internal: return py::return_value_policy::reference_internal;
156 case ::welder::rv_kind::none: break; // no pybind11 equivalent
157 }
158 return py::return_value_policy::automatic;
159 }
160
178 template <std::meta::info Fn, class Def, std::size_t... I, std::size_t... K>
179 static void _def_function(const char* name, Def def_into,
180 std::index_sequence<I...>, std::index_sequence<K...>) {
181 static constexpr auto names{::welder::detail::param_names<Fn>()};
182 static constexpr auto ka{::welder::detail::keep_alive_pairs<Fn>()};
184 constexpr ::welder::rv_kind rvk{::welder::return_policy_of(Fn, language)};
185 static_assert(rvk != ::welder::rv_kind::none,
186 "welder: return_policy 'none' has no pybind11 equivalent "
187 "(it is nanobind-only) — choose another policy");
188 constexpr py::return_value_policy rvp{_return_value_policy(rvk)};
191 if (doc.empty())
192 def_into(name, &[:Fn:], py::arg(names[I])..., rvp,
193 py::keep_alive<ka[K].nurse, ka[K].patient>()...);
194 else
195 def_into(name, &[:Fn:], doc.c_str(), py::arg(names[I])..., rvp,
196 py::keep_alive<ka[K].nurse, ka[K].patient>()...);
197 } else {
198 if (doc.empty())
199 def_into(name, &[:Fn:], rvp,
200 py::keep_alive<ka[K].nurse, ka[K].patient>()...);
201 else
202 def_into(name, &[:Fn:], doc.c_str(), rvp,
203 py::keep_alive<ka[K].nurse, ka[K].patient>()...);
204 }
205 }
206
216 template <std::meta::info Fn, class Self, class Def, std::size_t... I>
217 static void _def_truncated(const char* name, Def def_into,
218 std::index_sequence<I...>) {
219 // maybe_unused: a zero-arity truncation has an empty I pack, so the
220 // splices below never reference params and gcc flags it set-but-unused.
221 [[maybe_unused]] static constexpr auto params{
223 static constexpr auto names{::welder::detail::param_names<Fn>()};
224 constexpr py::return_value_policy rvp{
226 if constexpr (std::is_void_v<Self>) {
227 auto wrapper = [](typename [:params[I]:]... a) -> decltype(auto) {
228 return [:Fn:](std::forward<typename [:params[I]:]>(a)...);
229 };
231 def_into(name, wrapper, py::arg(names[I])..., rvp);
232 else
233 def_into(name, wrapper, rvp);
234 } else {
235 auto wrapper = [](Self& self,
236 typename [:params[I]:]... a) -> decltype(auto) {
237 return self.[:Fn:](std::forward<typename [:params[I]:]>(a)...);
238 };
240 def_into(name, wrapper, py::arg(names[I])..., rvp);
241 else
242 def_into(name, wrapper, rvp);
243 }
244 }
245
248 template <std::meta::info Fn, class Self, class Def, std::size_t... K>
249 static void _def_default_truncations(const char* name, Def def_into,
250 std::index_sequence<K...>) {
251 constexpr std::size_t P{std::meta::parameters_of(Fn).size()};
252 constexpr std::size_t D{sizeof...(K)};
253 (_def_truncated<Fn, Self>(name, def_into,
254 std::make_index_sequence<P - D + K>{}),
255 ...);
256 }
257
260 template <std::meta::info Fn, class Def>
261 static void _def_function(const char* name, Def def_into) {
263 name, def_into,
264 std::make_index_sequence<std::meta::parameters_of(Fn).size()>{},
265 std::make_index_sequence<
267 }
268
284 template <std::meta::info Ctor, std::size_t D, std::size_t... K>
285 static void _def_init_truncations(auto& cls, std::index_sequence<K...>) {
286 constexpr std::size_t P{std::meta::parameters_of(Ctor).size()};
287 (_def_init<Ctor>(cls, std::make_index_sequence<P - D + K>{}), ...);
288 }
289
290 template <std::meta::info Ctor, std::size_t... I>
291 static void _def_init(auto& cls, std::index_sequence<I...>) {
292 static constexpr auto params{::welder::detail::param_types<Ctor>()};
293 static constexpr auto names{::welder::detail::param_names<Ctor>()};
295 cls.def(py::init<typename [:params[I]:]...>(), py::arg(names[I])...);
296 else
297 cls.def(py::init<typename [:params[I]:]...>());
298 }
299
321 template <class T>
322 static py::object _copy_instance(py::handle self, py::object* memo) {
323 py::object cls{py::type::of(self)};
324 py::object out{cls.attr("__new__")(cls)};
325 if (memo)
326 (*memo)[py::int_(reinterpret_cast<std::uintptr_t>(self.ptr()))] =
327 out;
328 // Copy-construct the C++ payload into the fresh shell in place — the
329 // alias-aware work `py::init<const T&>` would do, done here so the copy
330 // needs no Python-visible `T(other)` constructor. A subclass shell
331 // (`Py_TYPE(out) != tinfo->type`) gets the trampoline so C++ virtual
332 // calls keep dispatching into the Python override; a plain instance
333 // gets T. init_instance then constructs the owning holder and registers
334 // the instance, exactly as pybind11's own constructor path finalizes.
335 namespace pd = py::detail;
336 const pd::type_info* tinfo{pd::get_type_info(typeid(T))};
337 auto* inst{reinterpret_cast<pd::instance*>(out.ptr())};
338 pd::value_and_holder v_h{inst->get_value_and_holder(tinfo)};
339 const T& src{py::cast<const T&>(self)};
340 v_h.value_ptr() = Py_TYPE(out.ptr()) != tinfo->type
341 ? static_cast<T*>(new construction_type<T>(src))
342 : new T(src);
343 tinfo->init_instance(inst, nullptr);
344 if (py::hasattr(self, "__dict__")) {
345 py::object d{self.attr("__dict__")};
346 if (memo)
347 d = py::module_::import("copy").attr("deepcopy")(d, *memo);
348 out.attr("__dict__").attr("update")(d);
349 }
350 for (py::handle name :
351 py::module_::import("copyreg").attr("_slotnames")(cls)) {
352 if (!py::hasattr(self, name))
353 continue; // an unassigned slot stays unassigned on the copy
354 py::object v{py::getattr(self, name)};
355 if (memo)
356 v = py::module_::import("copy").attr("deepcopy")(v, *memo);
357 py::setattr(out, name, v);
358 }
359 return out;
360 }
361
367 template <class T, std::size_t I>
368 static consteval bool _lazy_default() {
370 using field_type = std::remove_const_t<
371 typename [:std::meta::type_of(::welder::detail::aggregate_fields<T>()[I]):]>;
373 } else {
374 return false;
375 }
376 }
377
380 template <class T, std::size_t I>
381 using _init_param = std::conditional_t<
383 std::optional<std::remove_const_t<
384 typename [:std::meta::type_of(::welder::detail::aggregate_fields<T>()[I]):]>>,
385 std::remove_const_t<
386 typename [:std::meta::type_of(::welder::detail::aggregate_fields<T>()[I]):]>>;
387
394 template <class T, std::size_t I>
395 static auto _init_value(_init_param<T, I>&& arg) {
396 if constexpr (_lazy_default<T, I>()) {
397 constexpr auto field =
399 if (arg)
400 return typename _init_param<T, I>::value_type{std::move(*arg)};
401 return typename _init_param<T, I>::value_type{T{}.[:field:]};
402 } else {
403 return std::move(arg);
404 }
405 }
406
418 template <class T, std::size_t I>
419 static auto _aggregate_arg([[maybe_unused]] const T& probe) {
420 static constexpr auto fields{::welder::detail::aggregate_fields<T>()};
421 constexpr const char* name{
422 std::define_static_string(std::meta::identifier_of(fields[I]))};
423 if constexpr (_lazy_default<T, I>()) {
424 // A registration-needed default has no expression-shaped repr
425 // anyway — the signature spells it `...`; None (or omission)
426 // selects the NSDMI value in C++.
427 return py::arg_v(name, py::none(), "...");
428 } else if constexpr (I >= ::welder::detail::aggregate_defaults_from<T>()) {
429 return py::arg(name) = probe.[:fields[I]:];
430 } else {
431 return py::arg(name);
432 }
433 }
434
447 template <class T, std::size_t... I>
448 static void _def_aggregate_init(auto& cls, std::index_sequence<I...>) {
449 static constexpr auto fields{::welder::detail::aggregate_fields<T>()};
450 // The probe exists only when a default is extractable
451 // (aggregate_defaults_from guarantees T{} is well-formed then) —
452 // and T{} in the init lambda reuses the same guarantee.
454 fields.size()) {
455 const T probe{};
456 cls.def(py::init([](_init_param<T, I>... args) {
457 return T{_init_value<T, I>(std::move(args))...};
458 }),
459 _aggregate_arg<T, I>(probe)...);
460 } else {
461 cls.def(py::init(
462 [](typename [:std::meta::type_of(fields[I]):]... args) {
463 return T{std::move(args)...};
464 }),
465 py::arg(std::define_static_string(
466 std::meta::identifier_of(fields[I])))...);
467 }
468 }
469
484 static void _install_live_properties(py::module_& m, py::dict props) {
485 auto builtins{py::module_::import("builtins")};
486 auto subclass{builtins.attr("type")(
487 py::str("welder_live_module"),
488 py::make_tuple(m.attr("__class__")), props)};
489 // The dynamically-created class would otherwise carry
490 // `__module__ == "_frozen_importlib"` (the frame that ran module init).
491 // pybind11 copies a module *scope*'s `__module__` onto every function later
492 // defined on it, so any function welded onto `m` after this swap — e.g. a
493 // `weld_function` following a mutable `weld_variable` on the same handle —
494 // would be misattributed in generated stubs. Stamp the module's own name so
495 // those functions keep the right `__module__`.
496 subclass.attr("__module__") = m.attr("__name__");
497 m.attr("__class__") = subclass;
498 }
499
513 template <class T, class Trampoline, auto Bases, std::size_t... I>
514 static auto _make_class(py::handle scope, const char* name, const char* doc,
515 std::index_sequence<I...>) {
516 if constexpr (std::is_void_v<Trampoline>) {
517 if (doc)
518 return py::class_<T, typename [:Bases[I]:]...>(scope, name, doc);
519 return py::class_<T, typename [:Bases[I]:]...>(scope, name);
520 } else {
521 // The trampoline is an extra `py::class_` template argument; Python
522 // subclasses instantiate it, so their overrides capture C++ virtual calls.
523 if (doc)
524 return py::class_<T, Trampoline, typename [:Bases[I]:]...>(scope, name, doc);
525 return py::class_<T, Trampoline, typename [:Bases[I]:]...>(scope, name);
526 }
527 }
528
538 template <class T, auto Bases, std::size_t... I>
539 static auto _make_class_at(py::handle scope, const char* name,
540 const char* doc, std::index_sequence<I...> seq) {
541 namespace py_ = ::welder::rods::python;
542 if constexpr (::welder::has_virtual_methods(^^T)) {
543 // Resolve the trampoline: an explicit `trampoline_for<T>` wins; otherwise
544 // scan T's namespace for a `[[=trampoline]]`-annotated subclass.
545 constexpr auto scanned{py_::scanned_trampoline_of(^^T)};
546 static_assert(
547 py_::trampoline_for<T> != std::meta::info{} || !scanned.ambiguous,
548 "welder: more than one [[=welder::rods::python::trampoline]] class in "
549 "this namespace derives from T; disambiguate by specializing "
550 "welder::rods::python::trampoline_for<T>.");
551 constexpr std::meta::info tramp{py_::trampoline_for<T> != std::meta::info{}
552 ? py_::trampoline_for<T>
553 : scanned.type};
554 if constexpr (tramp != std::meta::info{}) {
555 using Trampoline = [:tramp:];
556 static_assert(
557 py_::trampoline_covers(^^T, ^^Trampoline),
558 "welder: the trampoline registered for this type does not "
559 "override all of its virtual methods; every virtual needs an "
560 "override forwarding to Python (see WELDER_PY_OVERRIDE).");
561 return _make_class<T, Trampoline, Bases>(scope, name, doc, seq);
562 } else {
563 static_assert(
565 "welder: this welded type has virtual methods but no trampoline "
566 "is registered, so a Python subclass could not override them. "
567 "Register one — a [[=welder::rods::python::trampoline]] subclass "
568 "in T's namespace, or a welder::rods::python::trampoline_for<T> "
569 "specialization — or annotate T with "
570 "[[=welder::bind_flat]] to bind it non-overridably.");
571 return _make_class<T, void, Bases>(scope, name, doc, seq);
572 }
573 } else {
574 return _make_class<T, void, Bases>(scope, name, doc, seq);
575 }
576 }
577
578 public:
579 // --- caster oracle + emission primitives (the welder::rod contract) --
580
584 template <class T>
586
589 static consteval const char* special_method_name(std::meta::info op_fn) {
590 return ::welder::rods::python::operator_dunder(op_fn);
591 }
592
593 // --- class binding ------------------------------------------------------
594
599 template <class T>
602
612 template <class T, auto Bases, std::size_t... I>
613 static auto make_class(module_type& m, const char* name, const char* doc,
614 std::index_sequence<I...> seq) {
615 return _make_class_at<T, Bases>(m, name, doc, seq);
616 }
617
623 template <class T, auto Bases, std::size_t... I>
624 static auto make_nested_class(module_type&, auto& outer_cls, const char* name,
625 const char* doc, std::index_sequence<I...> seq) {
626 return _make_class_at<T, Bases>(outer_cls, name, doc, seq);
627 }
628
647 template <class T, auto Ctors, bool HasDefault, bool Aggregate, bool Copyable,
648 class Style = ::welder::naming::none>
649 static void add_constructors(auto& cls) {
650 if constexpr (HasDefault)
651 cls.def(py::init<>());
652 if constexpr (Copyable) {
653 // A trampolined type must keep the copy faithful for Python
654 // subclasses: _copy_instance constructs the ALIAS payload on a
655 // subclass shell only if it is constructible from const T&.
656 static_assert(
657 std::is_constructible_v<construction_type<T>, const T&>,
658 "welder: this type's trampoline lacks a copy-from-base "
659 "constructor, so a copied Python-subclass instance would hold a "
660 "plain base payload and silently stop dispatching virtuals into "
661 "Python. The WELDER_PY_TRAMPOLINE(TRAMP, BASE) macro declares "
662 "it; a hand-rolled trampoline needs 'Tramp(const Base&)' — or "
663 "mark::exclude the copy constructor.");
664 // The copy constructor is exposed ONLY as Python's copy protocol —
665 // `copy.copy`/`copy.deepcopy` — never as a `T(other)` init overload
666 // (that C++-ism is unidiomatic in Python, and would collide with a
667 // one-arg user constructor). _copy_instance owns the in-place copy.
668 cls.def("__copy__", [](py::handle self) {
669 return _copy_instance<T>(self, nullptr);
670 });
671 cls.def(
672 "__deepcopy__",
673 [](py::handle self, py::object memo) {
674 return _copy_instance<T>(self, &memo);
675 },
676 py::arg("memo"));
677 }
678 template for (constexpr auto ctor : std::define_static_array(Ctors)) {
679 _def_init<ctor>(cls, std::make_index_sequence<
680 std::meta::parameters_of(ctor).size()>{});
681 constexpr std::size_t d{::welder::detail::trailing_default_count<ctor>()};
682 if constexpr (d > 0)
684 std::make_index_sequence<d>{});
685 }
686 if constexpr (Aggregate) {
687 constexpr auto fields{::welder::detail::aggregate_fields<T>()};
688 _def_aggregate_init<T>(cls, std::make_index_sequence<fields.size()>{});
689 }
690 }
691
703 template <std::meta::info Mem, class Style = ::welder::naming::none>
704 static void add_field(auto& cls) {
705 constexpr const char* name{
707 constexpr const char* doc{::welder::doc_of<Mem>()};
708 // Read-only either because the member's type is const (def_readwrite's
709 // setter would not compile) or because a `no_reassign` mark forces it on
710 // an otherwise-mutable member (a whole-object rebind is rejected; in-place
711 // mutation still writes through the reference_internal getter).
712 constexpr bool read_only{std::meta::is_const_type(std::meta::type_of(Mem)) ||
714 if constexpr (!std::meta::is_public(Mem)) {
715 // A protected member (admitted under policy::weld_protected) binds
716 // as a property over welder::detail::field_access — gcc-16 rejects
717 // the dependent `&[:Mem:]` for protected data (see field_access).
718 // Same semantics as def_readwrite: reference_internal getter.
720 if constexpr (read_only) {
721 if constexpr (doc)
722 cls.def_property_readonly(
723 name, &fa::get, py::return_value_policy::reference_internal,
724 doc);
725 else
726 cls.def_property_readonly(
727 name, &fa::get,
728 py::return_value_policy::reference_internal);
729 } else {
730 if constexpr (doc)
731 cls.def_property(name, &fa::get, &fa::set,
732 py::return_value_policy::reference_internal,
733 doc);
734 else
735 cls.def_property(name, &fa::get, &fa::set,
736 py::return_value_policy::reference_internal);
737 }
738 } else if constexpr (read_only) {
739 if constexpr (doc)
740 cls.def_readonly(name, &[:Mem:], doc);
741 else
742 cls.def_readonly(name, &[:Mem:]);
743 } else {
744 if constexpr (doc)
745 cls.def_readwrite(name, &[:Mem:], doc);
746 else
747 cls.def_readwrite(name, &[:Mem:]);
748 }
749 }
750
762 template <class T, std::meta::info Getter, std::meta::info Setter>
763 static void add_property(auto& cls, const char* name) {
765 constexpr ::welder::rv_kind rvk{::welder::return_policy_of(Getter, language)};
766 static_assert(rvk != ::welder::rv_kind::none,
767 "welder: return_policy 'none' has no pybind11 equivalent "
768 "(it is nanobind-only) — choose another policy");
769 constexpr const char* doc{::welder::doc_of<Getter>()};
770 auto def{[&](auto&&... extra) {
771 if constexpr (Setter == std::meta::info{}) {
772 cls.def_property_readonly(name, &[:Getter:],
773 std::forward<decltype(extra)>(extra)...);
774 } else if constexpr (std::is_void_v<
775 typename [:std::meta::return_type_of(Setter):]>) {
776 cls.def_property(name, &[:Getter:], &[:Setter:],
777 std::forward<decltype(extra)>(extra)...);
778 } else {
779 // A value-returning setter (a fluent T& set_x(…)): the property
780 // protocol has no slot for the return, so discard it — binding
781 // the member pointer directly would make pybind11 convert a
782 // value the gate deliberately never checked.
783 using Arg = typename
784 [:std::meta::type_of(std::meta::parameters_of(Setter)[0]):];
785 static constexpr auto sp{&[:Setter:]};
786 cls.def_property(
787 name, &[:Getter:],
788 [](T& self, Arg v) { (self.*sp)(std::forward<Arg>(v)); },
789 std::forward<decltype(extra)>(extra)...);
790 }
791 }};
792 if constexpr (rvk == ::welder::rv_kind::automatic) {
793 if constexpr (doc)
794 def(doc);
795 else
796 def();
797 } else {
798 constexpr py::return_value_policy rvp{_return_value_policy(rvk)};
799 if constexpr (doc)
800 def(rvp, doc);
801 else
802 def(rvp);
803 }
804 }
805
808 template <auto Fns, class Style = ::welder::naming::none>
809 static void add_method(auto& cls) {
810 constexpr const char* name{
812 template for (constexpr auto fn : std::define_static_array(Fns)) {
813 _def_function<fn>(name, [&cls](auto&&... a) {
814 cls.def(std::forward<decltype(a)>(a)...);
815 });
816 constexpr std::size_t d{::welder::detail::trailing_default_count<fn>()};
817 if constexpr (d > 0)
819 fn, typename std::remove_reference_t<decltype(cls)>::type>(
820 name,
821 [&cls](auto&&... a) {
822 cls.def(std::forward<decltype(a)>(a)...);
823 },
824 std::make_index_sequence<d>{});
825 }
826 }
827
829 template <auto Fns, class Style = ::welder::naming::none>
830 static void add_static_method(auto& cls) {
831 constexpr const char* name{
832 ::welder::name_of<Fns[0], language, Style,
834 template for (constexpr auto fn : std::define_static_array(Fns)) {
835 _def_function<fn>(name, [&cls](auto&&... a) {
836 cls.def_static(std::forward<decltype(a)>(a)...);
837 });
838 constexpr std::size_t d{::welder::detail::trailing_default_count<fn>()};
839 if constexpr (d > 0)
841 name,
842 [&cls](auto&&... a) {
843 cls.def_static(std::forward<decltype(a)>(a)...);
844 },
845 std::make_index_sequence<d>{});
846 }
847 }
848
859 template <class T, auto Fns>
860 static void add_operator(auto& cls) {
861 template for (constexpr auto fn : std::define_static_array(Fns)) {
862 if constexpr (::welder::detail::free_operator_reflected(fn, ^^T)) {
864 } else {
868 std::make_index_sequence<
870 }
871 }
872 }
873
884 template <class T, auto Fns, auto Covered>
885 static void add_comparisons(auto& cls) {
887 [&cls](const char* name, auto fp) {
888 cls.def(name, fp, py::is_operator{});
889 });
890 }
891
894 template <class T, std::meta::info Fn>
895 static void add_stringifier(auto& cls) {
896 cls.def("__str__", &::welder::detail::stringify<T, Fn>);
897 }
898
899 private:
907 template <std::meta::info Fn, bool NotImpl, class Cls, std::size_t... K>
908 static void _def_operator(const char* name, Cls& cls,
909 std::index_sequence<K...>) {
910 static constexpr auto ka{::welder::detail::keep_alive_pairs<Fn>()};
912 constexpr ::welder::rv_kind rvk{::welder::return_policy_of(Fn, language)};
913 static_assert(rvk != ::welder::rv_kind::none,
914 "welder: return_policy 'none' has no pybind11 equivalent "
915 "(it is nanobind-only) — choose another policy");
916 constexpr py::return_value_policy rvp{_return_value_policy(rvk)};
918 auto def{[&](auto&&... extra) {
919 if (doc.empty())
920 cls.def(name, &[:Fn:], rvp,
921 py::keep_alive<ka[K].nurse, ka[K].patient>()...,
922 std::forward<decltype(extra)>(extra)...);
923 else
924 cls.def(name, &[:Fn:], doc.c_str(), rvp,
925 py::keep_alive<ka[K].nurse, ka[K].patient>()...,
926 std::forward<decltype(extra)>(extra)...);
927 }};
928 if constexpr (NotImpl)
929 def(py::is_operator{});
930 else
931 def();
932 }
933
937 template <class T, std::meta::info Fn>
938 static void _def_reflected_operator(auto& cls) {
939 constexpr const char* name{::welder::rods::python::reflected_dunder(Fn)};
940 if constexpr (name != nullptr) {
942 using Lhs = typename
943 [:std::meta::type_of(std::meta::parameters_of(Fn)[0]):];
944 cls.def(
945 name,
946 [](const T& self, Lhs lhs) {
947 return [:Fn:](static_cast<Lhs&&>(lhs), self);
948 },
949 py::is_operator{});
950 }
951 }
952
953 public:
954
955 // --- enum binding -------------------------------------------------------
956
969 template <class E>
970 struct enum_handle {
971 py::object scope;
973 const char* name;
974 std::unique_ptr<py::native_enum<E>> impl;
975
979 void value(const char* n, E v) { impl->value(n, v); }
981 void export_values() { impl->export_values(); }
982
985 void finalize() {
986 impl->finalize(); // commits the enum onto `scope` as `name`
987 // pybind11 3.0.1 does not yet stamp the `__pybind11_native_enum__`
988 // marker that pybind11-stubgen keys on to recognize a stdlib-enum (and
989 // strip its `enum` internals from the generated .pyi); a later pybind11
990 // sets it. Set it ourselves so welded enums produce clean stubs — both
991 // welder's own and any run by a consumer. Harmless once pybind11 sets it
992 // too; drop when the conan pybind11 package carries it.
993 scope.attr(name).attr("__pybind11_native_enum__") = true;
994 }
995 };
996
1005 template <class T>
1007 std::declval<module_type&>(), nullptr, nullptr, std::index_sequence<>{}));
1008 template <class E> using enum_handle_type = enum_handle<E>;
1009
1020 template <class T>
1021 static class_handle_type<T> reopen_class(module_type& scope, const char* name) {
1022 return py::reinterpret_borrow<class_handle_type<T>>(scope.attr(name));
1023 }
1024
1027 template <class T>
1029 const char* name) {
1030 return py::reinterpret_borrow<class_handle_type<T>>(outer.attr(name));
1031 }
1032
1039 static std::string _enum_docstring(const ::welder::detail::enum_doc& ed) {
1040 return DocStyle::format_enum(ed);
1041 }
1042
1045 template <class E>
1046 static enum_handle<E> make_enum(module_type& m, const char* name,
1047 const ::welder::detail::enum_doc& ed) {
1048 // native_enum copies class_doc into a std::string (so the transient doc here
1049 // is safe) and treats "" as "leave __doc__ untouched" — matching the prior
1050 // nullptr behaviour when the enum carries no documentation.
1051 const std::string doc{_enum_docstring(ed)};
1052 return {m, name,
1053 std::make_unique<py::native_enum<E>>(m, name, "enum.IntEnum",
1054 doc.c_str())};
1055 }
1056
1062 template <class E>
1064 const char* name,
1065 const ::welder::detail::enum_doc& ed) {
1066 const std::string doc{_enum_docstring(ed)};
1067 return {outer_cls, name,
1068 std::make_unique<py::native_enum<E>>(outer_cls, name,
1069 "enum.IntEnum",
1070 doc.c_str())};
1071 }
1072
1074 template <std::meta::info Enum, class Style = ::welder::naming::none>
1075 static void add_enumerator(auto& e) {
1076 e.value(
1078 [:Enum:]);
1079 }
1080
1083 template <class E>
1084 static void finish_enum(auto& e) {
1085 // Mirror C++ scope semantics: an unscoped enum's enumerators are visible
1086 // unqualified, so export them into the enclosing scope; a scoped enum's
1087 // are reached as E.Value, so leave them scoped.
1088 if constexpr (!std::is_scoped_enum_v<E>)
1089 e.export_values();
1090 e.finalize(); // native_enum requires an explicit finalize()
1091 }
1092
1093 // --- namespace / module binding -----------------------------------------
1094
1098 static py::dict open_module(module_type&) { return py::dict{}; }
1099
1101 static void set_module_doc(module_type& m, const char* doc) { m.doc() = doc; }
1102
1110 template <auto Fns, class Style = ::welder::naming::none>
1111 static py::object add_function(module_type& m, const char* name = nullptr) {
1112 const char* fn_name{::welder::name_of_or<Fns[0], language, Style,
1114 template for (constexpr auto fn : std::define_static_array(Fns)) {
1115 _def_function<fn>(fn_name, [&m](auto&&... a) {
1116 m.def(std::forward<decltype(a)>(a)...);
1117 });
1118 constexpr std::size_t d{::welder::detail::trailing_default_count<fn>()};
1119 if constexpr (d > 0)
1121 fn_name,
1122 [&m](auto&&... a) { m.def(std::forward<decltype(a)>(a)...); },
1123 std::make_index_sequence<d>{});
1124 }
1125 return m.attr(fn_name);
1126 }
1127
1134 template <std::meta::info Var, class Style = ::welder::naming::none>
1135 static void add_variable(module_type& m, py::dict& live,
1136 const char* name_override = nullptr) {
1137 const char* name{
1139 name_override)};
1140 if constexpr (std::meta::is_const_type(std::meta::type_of(Var))) {
1141 m.attr(name) = [:Var:]; // immutable: a value snapshot at bind time
1142 } else {
1143 // Mutable: a live property over the C++ global. The descriptors take a
1144 // leading `self` (the module) and ignore it.
1145 auto property{py::module_::import("builtins").attr("property")};
1146 live[name] = property(
1147 py::cpp_function([](py::object) { return [:Var:]; }),
1148 py::cpp_function([](py::object,
1149 typename [:std::meta::type_of(Var):] v) {
1150 [:Var:] = v;
1151 }));
1152 }
1153 }
1154
1156 static module_type add_submodule(module_type& m, const char* name) {
1157 return m.def_submodule(name);
1158 }
1159
1180 template <class Container, class Style = ::welder::naming::none>
1181 static void bind_container(module_type& m, const char* name) {
1182 constexpr ::welder::container_kind kind{
1183 ::welder::container_kind_of(^^Container)};
1184 if constexpr (kind == ::welder::container_kind::sequence) {
1185 using Elem = typename Container::value_type;
1186 if constexpr (::welder::container_is_contiguous(^^Container) &&
1187 std::is_arithmetic_v<Elem> && !std::is_same_v<Elem, bool>) {
1188 // Contiguous scalar buffer: expose the buffer protocol so
1189 // numpy/memoryview/ctypes view data() zero-copy.
1190 auto cls{py::bind_vector<Container>(m, name, py::buffer_protocol())};
1193 } else if constexpr (::welder::container_is_contiguous(^^Container) &&
1195 // Contiguous POD-struct buffer: no scalar dtype, so expose the numpy
1196 // array-interface protocol (a structured, zero-copy, numpy-free view).
1197 auto cls{py::bind_vector<Container>(m, name)};
1201 } else {
1202 auto cls{py::bind_vector<Container>(m, name)};
1205 }
1206 } else if constexpr (kind == ::welder::container_kind::fixed_sequence) {
1207 // std::array has no py::bind_array; hand-write the vector protocol minus
1208 // the size-changing ops.
1209 _bind_array<Container>(m, name);
1210 } else {
1211 py::bind_map<Container>(m, name);
1212 }
1213 }
1214
1232 template <class Container>
1233 static void _bind_array(module_type& m, const char* name) {
1234 using Elem = typename Container::value_type;
1235 constexpr std::size_t N{std::tuple_size_v<Container>};
1236 constexpr bool scalar{std::is_arithmetic_v<Elem> &&
1237 !std::is_same_v<Elem, bool>};
1238 auto cls{[&] {
1239 if constexpr (scalar)
1240 return py::class_<Container>(m, name, py::buffer_protocol());
1241 else
1242 return py::class_<Container>(m, name);
1243 }()};
1244 cls.def(py::init<>());
1245 cls.def(py::init<const Container&>());
1246 // Construct from a length-N sequence, registered as an implicit conversion so
1247 // `def_readwrite`'s setter accepts a plain list/tuple (`obj.arr = [...]`).
1248 cls.def(py::init([](const py::sequence& seq) {
1249 if (py::len(seq) != N)
1250 throw py::value_error(
1251 "expected a sequence of the array's fixed length");
1252 Container a{};
1253 std::size_t i{0};
1254 for (py::handle h : seq)
1255 a[i++] = h.cast<Elem>();
1256 return a;
1257 }));
1258 py::implicitly_convertible<py::sequence, Container>();
1259 cls.def("__len__", [](const Container&) { return N; });
1260 cls.def(
1261 "__getitem__",
1262 [](Container& a, py::ssize_t i) -> Elem& {
1263 return a[_wrap_index(i, N)];
1264 },
1265 py::return_value_policy::reference_internal);
1266 cls.def("__setitem__", [](Container& a, py::ssize_t i, const Elem& v) {
1267 a[_wrap_index(i, N)] = v;
1268 });
1269 cls.def(
1270 "__iter__",
1271 [](Container& a) { return py::make_iterator(a.begin(), a.end()); },
1272 py::keep_alive<0, 1>());
1273 if constexpr (scalar) {
1274 // The buffer protocol: a 1-D contiguous view of data() (numpy/memoryview).
1275 cls.def_buffer([](Container& a) -> py::buffer_info {
1276 return py::buffer_info(a.data(),
1277 static_cast<py::ssize_t>(sizeof(Elem)),
1278 py::format_descriptor<Elem>::format(), 1,
1279 {a.size()}, {sizeof(Elem)});
1280 });
1281 } else if constexpr (::welder::rods::python::pod_array_eligible<Elem>()) {
1282 // Contiguous POD-struct element: structured, zero-copy numpy view.
1284 }
1285 }
1286
1290 static std::size_t _wrap_index(py::ssize_t i, std::size_t n) {
1291 if (i < 0)
1292 i += static_cast<py::ssize_t>(n);
1293 if (i < 0 || static_cast<std::size_t>(i) >= n)
1294 throw py::index_error("array index out of range");
1295 return static_cast<std::size_t>(i);
1296 }
1297
1298 protected:
1307 template <class Container, class Elem, class Cls>
1308 static void _def_sizing(Cls& cls) {
1309 using Size = typename Container::size_type;
1310 if constexpr (requires(Container& c, Size n) { c.reserve(n); })
1311 cls.def(
1312 "reserve", [](Container& v, Size n) { v.reserve(n); }, py::arg("n"),
1313 "Pre-allocate capacity for at least n elements (no-op if capacity "
1314 "already exceeds n). Prevents reallocation — and reference "
1315 "invalidation — across a following run of append()/new().");
1316 if constexpr (requires(Container& c, Size n) { c.resize(n); })
1317 cls.def(
1318 "resize", [](Container& v, Size n) { v.resize(n); }, py::arg("n"),
1319 "Resize to exactly n elements, value-initializing any new tail "
1320 "elements. Shrinking or reallocating invalidates references.");
1321 }
1322
1331 template <class Container, class Elem, class Cls>
1332 static void _def_new(Cls& cls) {
1333 if constexpr (std::is_class_v<Elem> &&
1334 std::is_default_constructible_v<Elem>)
1335 cls.def(
1336 "new", [](Container& v) -> Elem& { return v.emplace_back(); },
1337 py::return_value_policy::reference_internal,
1338 "Default-construct a new element in place at the end and return a "
1339 "live reference to it.");
1340 }
1341
1347 template <class Container, class Elem, class Cls>
1348 static void _array_interface(Cls& cls) {
1349 namespace pyai = ::welder::rods::python;
1350 cls.def_property_readonly("__array_interface__", [](Container& v) {
1351 static constexpr auto d{pyai::ai_descr<^^Elem>()};
1352 py::list descr{};
1353 for (auto [nm, ts] : d)
1354 descr.append(py::make_tuple(nm, ts));
1355 py::dict out{};
1356 out["shape"] = py::make_tuple(v.size());
1357 out["typestr"] = pyai::ai_typestr<Elem>();
1358 out["descr"] = descr;
1359 out["data"] = py::make_tuple(
1360 reinterpret_cast<std::uintptr_t>(v.data()), false);
1361 out["version"] = 3;
1362 return out;
1363 });
1364 }
1365
1366 public:
1367
1369 static void close_module(module_type& m, py::dict& live) {
1370 if (live.size() != 0)
1371 _install_live_properties(m, live);
1372 }
1373};
1374
1375static_assert(::welder::rod<rod<>>,
1376 "welder::rods::pybind11::rod<> must satisfy welder::rod");
1377
1378} // namespace welder::rods::pybind11
The NumPy array-interface descriptor for an opaque std::vector<T> whose element T is a plain-old-data...
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:263
The reference-semantic container table: which STL containers welder can bind opaquely* (by reference)...
Language-agnostic documentation layer: read [[=welder::doc(...)]] annotations off reflected entities ...
Docstring styles shared by welder's Python backends.
The stored forms of the annotation vocabulary.
consteval auto aggregate_fields()
The fields an aggregate is initialized from: its non-static data members in declaration order (all pu...
consteval std::size_t trailing_default_count()
How many TRAILING parameters of Fn carry a C++ default argument.
consteval auto param_types()
A function's parameter types, as a static array of reflections.
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 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 all_params_named()
Whether every parameter of Fn carries an identifier.
std::string stringify(const T &self)
The stringifier wrapper every runtime rod binds for a swept ostream inserter (see is_stringifier_for)...
consteval auto param_names()
A function's parameter names, in order.
consteval auto keep_alive_pairs()
The keep_alive dependencies declared on Fn, in declaration order.
consteval const char * operator_dunder(std::meta::info f)
The Python special-method ("dunder") name for an operator (member or anchored free),...
Definition operators.hpp:41
consteval const char * reflected_dunder(std::meta::info f)
The reflected ("swapped-operand") dunder for a free operator whose anchor type is the right operand —...
Definition operators.hpp:79
consteval bool dunder_uses_not_implemented(std::meta::info f)
Whether f's slot participates in Python's NotImplemented protocol — every binary arithmetic / bitwise...
consteval bool pod_array_eligible()
Is std::vector<E> viewable as a NumPy structured array — i.e.
void synthesize_comparisons(Def def)
The comparison-synthesis walk shared by both Python backends: for each operator<=> overload in Fns,...
consteval std::meta::info construction_type_of()
The type welder constructs when binding T: its registered/annotated trampoline if one exists,...
consteval bool has_virtual_methods(std::meta::info type)
Does type declare or inherit any overridable virtual method?
Definition virtuals.hpp:239
consteval bool member_no_reassign(std::meta::info member, lang L)
Is data member member bound read-only for L by a no_reassign mark?
Definition reflect.hpp:243
consteval detail::doc_spec< N > doc(const char(&s)[N])
Attach a docstring to a namespace, class, function, or function parameter.
consteval bool container_is_contiguous(std::meta::info type)
Is type a contiguous sequence — one whose elements live in a single block reachable via ....
consteval container_kind container_kind_of(std::meta::info type)
The opaque-binder kind of type.
lang
The target languages welder ships rods for — but not the whole value space.
Definition lang.hpp:42
@ py
Python (via the pybind11 and nanobind backends).
Definition lang.hpp:43
rv_kind
How a bound callable's returned object is owned/converted in the target language — welder's backend-n...
@ automatic
The rod default — emit no explicit policy.
@ none
Do not convert (nanobind rv_policy::none); pybind11 has no equivalent.
@ field
a data member → transform_field.
Definition naming.hpp:327
@ static_method
a static member function → transform_static_method.
Definition naming.hpp:325
@ function
a free function → transform_function.
Definition naming.hpp:326
@ fixed_sequence
A fixed-size sequence: std::array<T, N>.
@ sequence
A bind_vector container: std::vector.
consteval void validate_return_policy()
Reject a return_policy on Fn (for language L) that contradicts Fn's return type.
Definition reflect.hpp:361
consteval bool bound_flat(std::meta::info entity)
Does entity (a type or a member function) carry a bind_flat mark?
Definition virtuals.hpp:63
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:424
std::string function_docstring()
The complete docstring for function Fn under Style.
Definition doc.hpp:300
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:375
consteval const char * doc_of()
The doc text on Ent (a class, namespace, function, or parameter), or nullptr.
Definition doc.hpp:138
consteval rv_kind return_policy_of(std::meta::info fn, lang L)
The return-value policy declared on callable fn for language L.
Definition reflect.hpp:337
The C++-operator → Python special-method ("dunder") map shared by welder's Python backends.
Splice-based accessors for data member Mem — the pointer-to-member-free route the rods bind a protect...
The identity style: bind every C++ identifier unchanged.
Definition naming.hpp:259
Owning handle for a py::native_enum<E>, plus the scope + name to reach the finalized enum object.
Definition rod.hpp:970
void value(const char *n, E v)
Add enumerator n = v to the pending native enum.
Definition rod.hpp:979
py::object scope
the enclosing scope: a (sub)module, or — for a nested enum — the enclosing class handle
Definition rod.hpp:971
const char * name
the enum's Python name
Definition rod.hpp:973
void finalize()
Commit the enum onto scope as name, and stamp the pybind11-stubgen native-enum marker.
Definition rod.hpp:985
std::unique_ptr< py::native_enum< E > > impl
the (move-only) native enum
Definition rod.hpp:974
void export_values()
Export the enumerators into the enclosing scope (unscoped enums).
Definition rod.hpp:981
static void _bind_array(module_type &m, const char *name)
Bind fixed-size sequence Container (std::array<T, N>) opaquely — by reference, with element write-thr...
Definition rod.hpp:1233
decltype(make_class< T, std::array< std::meta::info, 0 >{}>( std::declval< module_type & >(), nullptr, nullptr, std::index_sequence<>{})) class_handle_type
The class / enum handles the per-class / per-enum hooks operate on — exactly what make_class / make_e...
Definition rod.hpp:1006
static auto make_nested_class(module_type &, auto &outer_cls, const char *name, const char *doc, std::index_sequence< I... > seq)
Create the py::class_ for a nested member type T, registered under its enclosing type's class handle ...
Definition rod.hpp:624
static constexpr lang language
welder::lang::py.
Definition rod.hpp:88
static void _def_sizing(Cls &cls)
Give the opaque sequence class cls the reserve(n) / resize(n) sizing methods (which bind_vector does ...
Definition rod.hpp:1308
static auto _aggregate_arg(const T &probe)
The py::arg for field I of aggregate T: named after the field and, for the defaultable NSDMI suffix (...
Definition rod.hpp:419
static void finish_enum(auto &e)
Finalize enum E: export an unscoped enum's values into the enclosing scope, then commit the enum to t...
Definition rod.hpp:1084
static void add_operator(auto &cls)
Bind operator slot group Fns — one (operator, arity) slot whole, member and anchored free entries mix...
Definition rod.hpp:860
static module_type add_submodule(module_type &m, const char *name)
Create a submodule named name under m.
Definition rod.hpp:1156
static void add_field(auto &cls)
Bind data member Mem as an attribute.
Definition rod.hpp:704
static void add_comparisons(auto &cls)
Synthesize the relational dunders from operator<=> group Fns: for each spaceship overload's operand t...
Definition rod.hpp:885
static auto _make_class(py::handle scope, const char *name, const char *doc, std::index_sequence< I... >)
Construct py::class_<T, NativeBases...> from a reflected base-type array.
Definition rod.hpp:514
static void add_method(auto &cls)
Bind method overload group Fns (name from Fns[0]; pybind11 chains one .def per overload and dispatche...
Definition rod.hpp:809
static void _def_default_truncations(const char *name, Def def_into, std::index_sequence< K... >)
Bind every omissible arity of Fn — arities P-D .
Definition rod.hpp:249
static void _def_function(const char *name, Def def_into)
Convenience overload: derive the parameter and keep_alive index sequences from Fn.
Definition rod.hpp:261
static void _def_new(Cls &cls)
Give the opaque std::vector<Elem> class cls a new() method that default-constructs an element in plac...
Definition rod.hpp:1332
static void _install_live_properties(py::module_ &m, py::dict props)
Give module m live get/set semantics for the names in props.
Definition rod.hpp:484
static constexpr bool has_native_caster
caster_oracle: T is convertible without welder registering a class for it iff pybind11 does not fall ...
Definition rod.hpp:585
static void _array_interface(Cls &cls)
Give the opaque std::vector<Elem> class cls a __array_interface__ property — the numpy array-interfac...
Definition rod.hpp:1348
static void set_module_doc(module_type &m, const char *doc)
Set the (sub)module docstring.
Definition rod.hpp:1101
static class_handle_type< T > reopen_nested_class(module_type &, auto &outer, const char *name)
The nested-scope form of reopen_class — retrieve T from its enclosing type's class handle (outer....
Definition rod.hpp:1028
static void _def_reflected_operator(auto &cls)
Bind reflected free operator Fn (T is its right operand) under its reflected dunder,...
Definition rod.hpp:938
static consteval py::return_value_policy _return_value_policy(::welder::rv_kind k)
Map welder's neutral welder::rv_kind to pybind11's return_value_policy.
Definition rod.hpp:147
static void add_constructors(auto &cls)
Bind T's whole constructor set (a chained-def framework just loops it): the default constructor when ...
Definition rod.hpp:649
static std::string _enum_docstring(const ::welder::detail::enum_doc &ed)
Assemble ed — the enum summary plus its documented enumerators — into the enum's class docstring unde...
Definition rod.hpp:1039
py::module_ module_type
pybind11's module handle.
Definition rod.hpp:89
static enum_handle< E > make_nested_enum(module_type &, auto &outer_cls, const char *name, const ::welder::detail::enum_doc &ed)
Create the enum_handle for a nested member enum E, scoped to its enclosing type's class handle — Pyth...
Definition rod.hpp:1063
static void close_module(module_type &m, py::dict &live)
Close the session: apply any accumulated live properties.
Definition rod.hpp:1369
static py::object _copy_instance(py::handle self, py::object *memo)
The subclass-faithful engine behind __copy__/__deepcopy__.
Definition rod.hpp:322
static class_handle_type< T > reopen_class(module_type &scope, const char *name)
Retrieve the ALREADY-registered class T as a fillable handle — the two-phase binding hook (welder::ca...
Definition rod.hpp:1021
static auto _make_class_at(py::handle scope, const char *name, const char *doc, std::index_sequence< I... > seq)
The trampoline-aware class factory over an arbitrary registration scope — the shared body of make_cla...
Definition rod.hpp:539
std::conditional_t< _lazy_default< T, I >(), std::optional< std::remove_const_t< typename[:std::meta::type_of(::welder::detail::aggregate_fields< T >()[I]):]> >, std::remove_const_t< typename[:std::meta::type_of(::welder::detail::aggregate_fields< T >()[I]):]> > _init_param
The synthesized constructor's parameter type for field I of T: std::optional<F> for a lazy default,...
Definition rod.hpp:381
static void _def_init(auto &cls, std::index_sequence< I... >)
Definition rod.hpp:291
static void _def_truncated(const char *name, Def def_into, std::index_sequence< I... >)
Bind ONE truncated overload of Fn taking its first sizeof...(I) parameters — the pybind11 mirror of t...
Definition rod.hpp:217
[:::welder::rods::python::construction_type_of< T >() :] construction_type
The type welder constructs when binding T — its registered trampoline if one exists,...
Definition rod.hpp:600
static constexpr bool _needs_registration
Whether pybind11 can only convert T via runtime class registration.
Definition rod.hpp:134
static py::object add_function(module_type &m, const char *name=nullptr)
Bind free-function overload group Fns as one module-level function (name from Fns[0]; one chained ....
Definition rod.hpp:1111
enum_handle< E > enum_handle_type
Definition rod.hpp:1008
static void add_static_method(auto &cls)
Bind static-method overload group Fns.
Definition rod.hpp:830
static void _def_function(const char *name, Def def_into, std::index_sequence< I... >, std::index_sequence< K... >)
Register the function/method reflected by Fn onto a pybind11 target.
Definition rod.hpp:179
static void add_stringifier(auto &cls)
Bind the swept free ostream inserter Fn as __str__ (via welder::detail::stringify).
Definition rod.hpp:895
static consteval const char * special_method_name(std::meta::info op_fn)
Map a member operator to its Python dunder (nullptr = not exposed).
Definition rod.hpp:589
static auto make_class(module_type &m, const char *name, const char *doc, std::index_sequence< I... > seq)
Create the py::class_<T, Bases…> handle, weaving in a trampoline when T is a welded virtual type with...
Definition rod.hpp:613
static void add_variable(module_type &m, py::dict &live, const char *name_override=nullptr)
Bind namespace variable Var as a module attribute.
Definition rod.hpp:1135
static consteval bool _lazy_default()
Whether field I of aggregate T binds its NSDMI default LAZILY — the same rule (and reason) as the nan...
Definition rod.hpp:368
static void _def_aggregate_init(auto &cls, std::index_sequence< I... >)
Synthesize a field constructor for a baseless aggregate T.
Definition rod.hpp:448
static py::dict open_module(module_type &)
Open a per-module session: a dict accumulating live (mutable-variable) properties; _install_live_prop...
Definition rod.hpp:1098
static void add_property(auto &cls, const char *name)
Bind the resolved property (Getter + optional Setter) as a Python property named name (driver-resolve...
Definition rod.hpp:763
static void _def_operator(const char *name, Cls &cls, std::index_sequence< K... >)
Def operator Fn (member, or free with the anchor on the left) under dunder name.
Definition rod.hpp:908
static void bind_container(module_type &m, const char *name)
Bind STL Container opaquely — by reference, with live mutation — under name, the driver's route for a...
Definition rod.hpp:1181
static std::size_t _wrap_index(py::ssize_t i, std::size_t n)
Normalize a Python index i (allowing one level of negative wrap-around) against length n,...
Definition rod.hpp:1290
static auto _init_value(_init_param< T, I > &&arg)
The value brace-initializing field I of T from constructor argument arg: the argument itself for a pl...
Definition rod.hpp:395
static enum_handle< E > make_enum(module_type &m, const char *name, const ::welder::detail::enum_doc &ed)
Create the enum_handle for E; ed's summary + enumerator docs become its class docstring (see _enum_do...
Definition rod.hpp:1046
static void _def_init_truncations(auto &cls, std::index_sequence< K... >)
Register py::init<P0, P1, …>() for constructor Ctor.
Definition rod.hpp:285
static void add_enumerator(auto &e)
Add enumerator Enum to the enum handle.
Definition rod.hpp:1075
Virtual-function overriding support shared by welder's Python backends.
welder's binding entry point: the welder::welder struct.