welder 0.1.0
Bindings for annotated C++ types, from C++26 reflection
Loading...
Searching...
No Matches
trampoline.hpp
Go to the documentation of this file.
1#pragma once
2#include <cstddef>
3#include <meta>
4#include <type_traits>
5#include <utility>
6
7#include <welder/rods/python/trampoline.hpp> // virtual_slot_count, trampoline_for, …
8
9#include <pybind11/pybind11.h>
10
28
29namespace welder::inline v0::rods::pybind11 {
30
31// The unqualified name `pybind11` resolves to *this* namespace inside it; alias the
32// real library the way pybind11's own docs do (see rod.hpp).
33namespace py = ::pybind11;
34
63template <auto Fn, typename Self, typename BaseCall, typename... Args>
64decltype(auto) override_dispatch(const Self& self, BaseCall&& base_call,
65 Args&&... args) {
66 using ret_type = [:std::meta::return_type_of(Fn):];
67 static_assert(
68 !std::is_reference_v<ret_type>,
69 "welder: cannot trampoline a virtual method that returns a reference "
70 "(pybind11 cannot keep the referent alive across the C++/Python boundary); "
71 "return by value, or mark the type [[=welder::rods::python::bind_flat]].");
72
73 constexpr const char* name{
74 std::define_static_string(std::meta::identifier_of(Fn))};
75
76 py::gil_scoped_acquire gil;
77 py::function override{py::get_override(&self, name)};
78 if (override)
79 return py::detail::cast_safe<ret_type>(
80 override(std::forward<Args>(args)...));
81
82 if constexpr (std::meta::is_pure_virtual(Fn))
83 // No base to fall back to and no Python override: pybind11's own diagnostic.
84 py::pybind11_fail(
85 "welder: tried to call a pure virtual method with no Python override");
86 else
87 return std::forward<BaseCall>(base_call)(std::forward<Args>(args)...);
88}
89
90} // namespace welder::rods::pybind11
91
98#define WELDER_PY_TRAMPOLINE(BASE) \
99 using welder_py_base = BASE; \
100 using welder_py_base::welder_py_base
101
114#define WELDER_PY_OVERRIDE_AS(SLOT, FUNC, ...) \
115 return ::welder::rods::pybind11::override_dispatch<(SLOT)>( \
116 static_cast<const welder_py_base&>(*this), \
117 [&](auto&&... _welder_a) -> decltype(auto) { \
118 return welder_py_base::FUNC( \
119 static_cast<decltype(_welder_a)&&>(_welder_a)...); \
120 } __VA_OPT__(, ) __VA_ARGS__)
121
130#define WELDER_PY_OVERRIDE(FUNC, ...) \
131 WELDER_PY_OVERRIDE_AS(^^welder_py_base::FUNC, FUNC __VA_OPT__(, ) __VA_ARGS__)
decltype(auto) override_dispatch(const Self &self, BaseCall &&base_call, Args &&... args)
Dispatch a captured virtual call to its Python override, else to the C++ base.
@ py
Python (via the pybind11 and nanobind backends).
Definition lang.hpp:43
Virtual-function overriding support shared by welder's Python backends.