welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
trampoline.hpp File Reference

pybind11's half of welder's virtual-override support: the reflection-driven dispatch helper that replaces PYBIND11_OVERRIDE / PYBIND11_OVERRIDE_PURE, and the WELDER_PY_TRAMPOLINE / WELDER_PY_OVERRIDE authoring macros. More...

#include <cstddef>
#include <meta>
#include <type_traits>
#include <utility>
#include <welder/rods/python/trampoline.hpp>
#include <pybind11/pybind11.h>
Include dependency graph for trampoline.hpp:

Go to the source code of this file.

Namespaces

namespace  welder
namespace  welder::rods
namespace  welder::rods::pybind11

Macros

#define WELDER_PY_TRAMPOLINE(TRAMP, BASE)
 Declare class TRAMP a pybind11 trampoline for BASE.
#define WELDER_PY_OVERRIDE_AS(SLOT, FUNC, ...)
 Body of a single virtual override in a WELDER_PY_TRAMPOLINE class, keyed on an explicit slot reflection.
#define WELDER_PY_OVERRIDE(FUNC, ...)
 Body of a single virtual override in a WELDER_PY_TRAMPOLINE class.

Functions

template<auto Fn, typename Self, typename BaseCall, typename... Args>
decltype(auto) welder::rods::pybind11::override_dispatch (const Self &self, BaseCall &&base_call, Args &&... args)
 Dispatch a captured virtual call to its Python override, else to the C++ base.

Detailed Description

pybind11's half of welder's virtual-override support: the reflection-driven dispatch helper that replaces PYBIND11_OVERRIDE / PYBIND11_OVERRIDE_PURE, and the WELDER_PY_TRAMPOLINE / WELDER_PY_OVERRIDE authoring macros.

The backend-neutral machinery (slot count, the trampoline_for registration hook, the coverage check) lives in <welder/rods/python/trampoline.hpp>. This header only adds what is specific to pybind11: get_override lookup and cast_safe. Unlike nanobind, pybind11 keeps no per-instance trampoline storage — the override is found from the C++ this pointer — so WELDER_PY_TRAMPOLINE injects only the base alias and inherited constructors. The macros are spelled the same as nanobind's (a translation unit includes exactly one Python rod), so a single trampoline definition compiles under either backend.

Include this from a pybind11 binding translation unit before the header that defines your trampoline subclass.

Definition in file trampoline.hpp.

Macro Definition Documentation

◆ WELDER_PY_OVERRIDE

#define WELDER_PY_OVERRIDE ( FUNC,
... )
Value:
WELDER_PY_OVERRIDE_AS(^^welder_py_base::FUNC, FUNC __VA_OPT__(, ) __VA_ARGS__)
#define WELDER_PY_OVERRIDE_AS(SLOT, FUNC,...)
Body of a single virtual override in a WELDER_PY_TRAMPOLINE class, keyed on an explicit slot reflecti...

Body of a single virtual override in a WELDER_PY_TRAMPOLINE class.

Use as the whole override body: int legs() const override { WELDER_PY_OVERRIDE(legs); }. Forwards to the Python override of FUNC if present, else to BASE::FUNC. Extra arguments are the method's parameters, forwarded to both paths. FUNC must name a single* virtual — for an overloaded one use WELDER_PY_OVERRIDE_AS with a welder::virtual_slot selection. Neutral name: each Python rod defines it against its own dispatch.

Definition at line 146 of file trampoline.hpp.

◆ WELDER_PY_OVERRIDE_AS

#define WELDER_PY_OVERRIDE_AS ( SLOT,
FUNC,
... )
Value:
return ::welder::rods::pybind11::override_dispatch<(SLOT)>( \
static_cast<const welder_py_base&>(*this), \
[&](auto&&... _welder_a) -> decltype(auto) { \
return welder_py_base::FUNC( \
static_cast<decltype(_welder_a)&&>(_welder_a)...); \
} __VA_OPT__(, ) __VA_ARGS__)

Body of a single virtual override in a WELDER_PY_TRAMPOLINE class, keyed on an explicit slot reflection.

The general form behind WELDER_PY_OVERRIDE, for the case the plain macro cannot spell: an overloaded virtual, where ^^welder_py_base::FUNC would name an overload set (ill-formed — P2996 has no overload-set reflection). SLOT is a reflection of the one base virtual this override implements — select it with welder::virtual_slot — and drives the dispatch (name, return type, pureness); the textual FUNC only spells the qualified base-class fallback call, where ordinary overload resolution picks the right overload from the forwarded parameters. Parenthesize SLOT if the expression contains commas. welder's generated trampolines use this form for every override.

Definition at line 130 of file trampoline.hpp.

◆ WELDER_PY_TRAMPOLINE

#define WELDER_PY_TRAMPOLINE ( TRAMP,
BASE )
Value:
using welder_py_base = BASE; \
TRAMP() = default; \
template <class WelderSrc = BASE> \
requires ::std::is_copy_constructible_v<WelderSrc> \
explicit TRAMP(const WelderSrc& welder_src) \
: welder_py_base(welder_src) {} \
using welder_py_base::welder_py_base

Declare class TRAMP a pybind11 trampoline for BASE.

Place at the top of a trampoline subclass body. Introduces the welder_py_base alias the override macro keys off, inherits BASE's constructors, and — the reason the macro takes the trampoline's own name — declares the two constructors inherited-constructor syntax cannot provide: a defaulted default constructor (any user-declared constructor would otherwise suppress it) and a guarded copy-from-base constructor. The latter is what keeps the copy protocol faithful for Python subclasses: __copy__/__deepcopy__ re-run the registered copy __init__ on a type(self).__new__ shell, and pybind11 then needs the alias type constructible from const BASE& — or the copied subclass instance would hold a plain BASE payload and stop dispatching virtuals into Python. It is a constrained template, so a noncopyable BASE simply leaves the trampoline non-copy-constructible (no error). pybind11 needs no per-instance storage (the override is found from this), so — unlike the nanobind spelling — this adds no data member. Neutral name across the Python rods.

Definition at line 109 of file trampoline.hpp.