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
1047 template <class E>
1048 static consteval const char* _enum_base() {
1049 return ::welder::flags_enum(^^E) ? "enum.IntFlag" : "enum.IntEnum";
1050 }
1051
1054 template <class E>
1055 static enum_handle<E> make_enum(module_type& m, const char* name,
1056 const ::welder::detail::enum_doc& ed) {
1057 // native_enum copies class_doc into a std::string (so the transient doc here
1058 // is safe) and treats "" as "leave __doc__ untouched" — matching the prior
1059 // nullptr behaviour when the enum carries no documentation.
1060 const std::string doc{_enum_docstring(ed)};
1061 return {m, name,
1062 std::make_unique<py::native_enum<E>>(m, name, _enum_base<E>(),
1063 doc.c_str())};
1064 }
1065
1071 template <class E>
1073 const char* name,
1074 const ::welder::detail::enum_doc& ed) {
1075 const std::string doc{_enum_docstring(ed)};
1076 return {outer_cls, name,
1077 std::make_unique<py::native_enum<E>>(outer_cls, name,
1078 _enum_base<E>(),
1079 doc.c_str())};
1080 }
1081
1083 template <std::meta::info Enum, class Style = ::welder::naming::none>
1084 static void add_enumerator(auto& e) {
1085 e.value(
1087 [:Enum:]);
1088 }
1089
1092 template <class E>
1093 static void finish_enum(auto& e) {
1094 // Mirror C++ scope semantics: an unscoped enum's enumerators are visible
1095 // unqualified, so export them into the enclosing scope; a scoped enum's
1096 // are reached as E.Value, so leave them scoped.
1097 if constexpr (!std::is_scoped_enum_v<E>)
1098 e.export_values();
1099 e.finalize(); // native_enum requires an explicit finalize()
1100 }
1101
1102 // --- namespace / module binding -----------------------------------------
1103
1107 static py::dict open_module(module_type&) { return py::dict{}; }
1108
1110 static void set_module_doc(module_type& m, const char* doc) { m.doc() = doc; }
1111
1119 template <auto Fns, class Style = ::welder::naming::none>
1120 static py::object add_function(module_type& m, const char* name = nullptr) {
1121 const char* fn_name{::welder::name_of_or<Fns[0], language, Style,
1123 template for (constexpr auto fn : std::define_static_array(Fns)) {
1124 _def_function<fn>(fn_name, [&m](auto&&... a) {
1125 m.def(std::forward<decltype(a)>(a)...);
1126 });
1127 constexpr std::size_t d{::welder::detail::trailing_default_count<fn>()};
1128 if constexpr (d > 0)
1130 fn_name,
1131 [&m](auto&&... a) { m.def(std::forward<decltype(a)>(a)...); },
1132 std::make_index_sequence<d>{});
1133 }
1134 return m.attr(fn_name);
1135 }
1136
1143 template <std::meta::info Var, class Style = ::welder::naming::none>
1144 static void add_variable(module_type& m, py::dict& live,
1145 const char* name_override = nullptr) {
1146 const char* name{
1148 name_override)};
1149 if constexpr (std::meta::is_const_type(std::meta::type_of(Var))) {
1150 m.attr(name) = [:Var:]; // immutable: a value snapshot at bind time
1151 } else {
1152 // Mutable: a live property over the C++ global. The descriptors take a
1153 // leading `self` (the module) and ignore it.
1154 auto property{py::module_::import("builtins").attr("property")};
1155 live[name] = property(
1156 py::cpp_function([](py::object) { return [:Var:]; }),
1157 py::cpp_function([](py::object,
1158 typename [:std::meta::type_of(Var):] v) {
1159 [:Var:] = v;
1160 }));
1161 }
1162 }
1163
1165 static module_type add_submodule(module_type& m, const char* name) {
1166 return m.def_submodule(name);
1167 }
1168
1189 template <class Container, class Style = ::welder::naming::none>
1190 static void bind_container(module_type& m, const char* name) {
1191 constexpr ::welder::container_kind kind{
1192 ::welder::container_kind_of(^^Container)};
1193 if constexpr (kind == ::welder::container_kind::sequence) {
1194 using Elem = typename Container::value_type;
1195 if constexpr (::welder::container_is_contiguous(^^Container) &&
1196 std::is_arithmetic_v<Elem> && !std::is_same_v<Elem, bool>) {
1197 // Contiguous scalar buffer: expose the buffer protocol so
1198 // numpy/memoryview/ctypes view data() zero-copy.
1199 auto cls{py::bind_vector<Container>(m, name, py::buffer_protocol())};
1202 } else if constexpr (::welder::container_is_contiguous(^^Container) &&
1204 // Contiguous POD-struct buffer: no scalar dtype, so expose the numpy
1205 // array-interface protocol (a structured, zero-copy, numpy-free view).
1206 auto cls{py::bind_vector<Container>(m, name)};
1210 } else {
1211 auto cls{py::bind_vector<Container>(m, name)};
1214 }
1215 } else if constexpr (kind == ::welder::container_kind::fixed_sequence) {
1216 // std::array has no py::bind_array; hand-write the vector protocol minus
1217 // the size-changing ops.
1218 _bind_array<Container>(m, name);
1219 } else {
1220 py::bind_map<Container>(m, name);
1221 }
1222 }
1223
1241 template <class Container>
1242 static void _bind_array(module_type& m, const char* name) {
1243 using Elem = typename Container::value_type;
1244 constexpr std::size_t N{std::tuple_size_v<Container>};
1245 constexpr bool scalar{std::is_arithmetic_v<Elem> &&
1246 !std::is_same_v<Elem, bool>};
1247 auto cls{[&] {
1248 if constexpr (scalar)
1249 return py::class_<Container>(m, name, py::buffer_protocol());
1250 else
1251 return py::class_<Container>(m, name);
1252 }()};
1253 cls.def(py::init<>());
1254 cls.def(py::init<const Container&>());
1255 // Construct from a length-N sequence, registered as an implicit conversion so
1256 // `def_readwrite`'s setter accepts a plain list/tuple (`obj.arr = [...]`).
1257 cls.def(py::init([](const py::sequence& seq) {
1258 if (py::len(seq) != N)
1259 throw py::value_error(
1260 "expected a sequence of the array's fixed length");
1261 Container a{};
1262 std::size_t i{0};
1263 for (py::handle h : seq)
1264 a[i++] = h.cast<Elem>();
1265 return a;
1266 }));
1267 py::implicitly_convertible<py::sequence, Container>();
1268 cls.def("__len__", [](const Container&) { return N; });
1269 cls.def(
1270 "__getitem__",
1271 [](Container& a, py::ssize_t i) -> Elem& {
1272 return a[_wrap_index(i, N)];
1273 },
1274 py::return_value_policy::reference_internal);
1275 cls.def("__setitem__", [](Container& a, py::ssize_t i, const Elem& v) {
1276 a[_wrap_index(i, N)] = v;
1277 });
1278 cls.def(
1279 "__iter__",
1280 [](Container& a) { return py::make_iterator(a.begin(), a.end()); },
1281 py::keep_alive<0, 1>());
1282 if constexpr (scalar) {
1283 // The buffer protocol: a 1-D contiguous view of data() (numpy/memoryview).
1284 cls.def_buffer([](Container& a) -> py::buffer_info {
1285 return py::buffer_info(a.data(),
1286 static_cast<py::ssize_t>(sizeof(Elem)),
1287 py::format_descriptor<Elem>::format(), 1,
1288 {a.size()}, {sizeof(Elem)});
1289 });
1290 } else if constexpr (::welder::rods::python::pod_array_eligible<Elem>()) {
1291 // Contiguous POD-struct element: structured, zero-copy numpy view.
1293 }
1294 }
1295
1299 static std::size_t _wrap_index(py::ssize_t i, std::size_t n) {
1300 if (i < 0)
1301 i += static_cast<py::ssize_t>(n);
1302 if (i < 0 || static_cast<std::size_t>(i) >= n)
1303 throw py::index_error("array index out of range");
1304 return static_cast<std::size_t>(i);
1305 }
1306
1307 protected:
1316 template <class Container, class Elem, class Cls>
1317 static void _def_sizing(Cls& cls) {
1318 using Size = typename Container::size_type;
1319 if constexpr (requires(Container& c, Size n) { c.reserve(n); })
1320 cls.def(
1321 "reserve", [](Container& v, Size n) { v.reserve(n); }, py::arg("n"),
1322 "Pre-allocate capacity for at least n elements (no-op if capacity "
1323 "already exceeds n). Prevents reallocation — and reference "
1324 "invalidation — across a following run of append()/new().");
1325 if constexpr (requires(Container& c, Size n) { c.resize(n); })
1326 cls.def(
1327 "resize", [](Container& v, Size n) { v.resize(n); }, py::arg("n"),
1328 "Resize to exactly n elements, value-initializing any new tail "
1329 "elements. Shrinking or reallocating invalidates references.");
1330 }
1331
1340 template <class Container, class Elem, class Cls>
1341 static void _def_new(Cls& cls) {
1342 if constexpr (std::is_class_v<Elem> &&
1343 std::is_default_constructible_v<Elem>)
1344 cls.def(
1345 "new", [](Container& v) -> Elem& { return v.emplace_back(); },
1346 py::return_value_policy::reference_internal,
1347 "Default-construct a new element in place at the end and return a "
1348 "live reference to it.");
1349 }
1350
1356 template <class Container, class Elem, class Cls>
1357 static void _array_interface(Cls& cls) {
1358 namespace pyai = ::welder::rods::python;
1359 cls.def_property_readonly("__array_interface__", [](Container& v) {
1360 static constexpr auto d{pyai::ai_descr<^^Elem>()};
1361 py::list descr{};
1362 for (auto [nm, ts] : d)
1363 descr.append(py::make_tuple(nm, ts));
1364 py::dict out{};
1365 out["shape"] = py::make_tuple(v.size());
1366 out["typestr"] = pyai::ai_typestr<Elem>();
1367 out["descr"] = descr;
1368 out["data"] = py::make_tuple(
1369 reinterpret_cast<std::uintptr_t>(v.data()), false);
1370 out["version"] = 3;
1371 return out;
1372 });
1373 }
1374
1375 public:
1376
1378 static void close_module(module_type& m, py::dict& live) {
1379 if (live.size() != 0)
1380 _install_live_properties(m, live);
1381 }
1382};
1383
1384static_assert(::welder::rod<rod<>>,
1385 "welder::rods::pybind11::rod<> must satisfy welder::rod");
1386
1387} // 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:255
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:373
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:349
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:1242
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:1317
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:1093
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:1165
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:1341
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:1357
static void set_module_doc(module_type &m, const char *doc)
Set the (sub)module docstring.
Definition rod.hpp:1110
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:1072
static void close_module(module_type &m, py::dict &live)
Close the session: apply any accumulated live properties.
Definition rod.hpp:1378
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:1120
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 consteval const char * _enum_base()
The stdlib base a welded enum binds as: enum.IntFlag for a [[=welder::flags]] bitmask enum (combined ...
Definition rod.hpp:1048
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:1144
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:1107
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:1190
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:1299
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:1055
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:1084
Virtual-function overriding support shared by welder's Python backends.
welder's binding entry point: the welder::welder struct.