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 <nanobind/nanobind.h>
10#include <nanobind/trampoline.h>
11
27
28namespace welder::inline v0::rods::nanobind {
29
30// The unqualified name `nanobind` resolves to *this* namespace inside it; alias the
31// real library the way nanobind's own docs do (see rod.hpp).
32namespace nb = ::nanobind;
33
54template <auto Fn, std::size_t N, typename BaseCall, typename... Args>
55decltype(auto) override_dispatch(const nb::detail::trampoline<N>& tr,
56 BaseCall&& base_call, Args&&... args) {
57 using ret_type = [:std::meta::return_type_of(Fn):];
58 static_assert(
59 !std::is_reference_v<ret_type>,
60 "welder: cannot trampoline a virtual method that returns a reference "
61 "(nanobind cannot keep the referent alive across the C++/Python boundary); "
62 "return by value, or mark the type [[=welder::rods::python::bind_flat]].");
63
64 constexpr const char* name{
65 std::define_static_string(std::meta::identifier_of(Fn))};
66
67 if constexpr (std::meta::is_pure_virtual(Fn)) {
68 // Pure: there is no base to fall back to. nanobind's trampoline machinery
69 // raises a descriptive error if Python supplied no override.
70 nb::detail::ticket t(tr, name, /*pure=*/true);
71 return nb::cast<ret_type>(tr.base().attr(t.key)(std::forward<Args>(args)...));
72 } else {
73 nb::detail::ticket t(tr, name, /*pure=*/false);
74 if (t.key.is_valid())
75 return nb::cast<ret_type>(
76 tr.base().attr(t.key)(std::forward<Args>(args)...));
77 return std::forward<BaseCall>(base_call)(std::forward<Args>(args)...);
78 }
79}
80
81} // namespace welder::rods::nanobind
82
90#define WELDER_PY_TRAMPOLINE(BASE) \
91 using welder_py_base = BASE; \
92 using welder_py_base::welder_py_base; \
93 ::nanobind::detail::trampoline< \
94 ::welder::rods::python::virtual_slot_count(^^BASE)> \
95 welder_nb_trampoline{this}
96
109#define WELDER_PY_OVERRIDE_AS(SLOT, FUNC, ...) \
110 return ::welder::rods::nanobind::override_dispatch<(SLOT)>( \
111 this->welder_nb_trampoline, \
112 [&](auto&&... _welder_a) -> decltype(auto) { \
113 return welder_py_base::FUNC( \
114 static_cast<decltype(_welder_a)&&>(_welder_a)...); \
115 } __VA_OPT__(, ) __VA_ARGS__)
116
125#define WELDER_PY_OVERRIDE(FUNC, ...) \
126 WELDER_PY_OVERRIDE_AS(^^welder_py_base::FUNC, FUNC __VA_OPT__(, ) __VA_ARGS__)
decltype(auto) override_dispatch(const nb::detail::trampoline< N > &tr, BaseCall &&base_call, Args &&... args)
Dispatch a captured virtual call to its Python override, else to the C++ base.
Virtual-function overriding support shared by welder's Python backends.