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 <string_view>
5#include <vector>
6
7#include <welder/diag.hpp> // the consteval diagnostics (no_matching_virtual_slot)
8#include <welder/vocabulary.hpp> // the annotation vocabulary (for structural specs)
9
50
51namespace welder::inline v0::rods::python {
52
53// --- bind_flat: opt out of trampoline support for a virtual type -------------
54
62
78inline constexpr bind_flat_spec bind_flat{};
79
83consteval bool bound_flat(std::meta::info entity) {
84 return !std::meta::annotations_of_with_type(entity, ^^bind_flat_spec).empty();
85}
86
87// --- trampoline registration -------------------------------------------------
88
103template <class T>
104constexpr std::meta::info trampoline_for = std::meta::info{};
105
106// --- trampoline discovery by annotation -------------------------------------
107
111
123inline constexpr trampoline_spec trampoline{};
124
128consteval bool is_trampoline(std::meta::info type) {
129 return !std::meta::annotations_of_with_type(type, ^^trampoline_spec).empty();
130}
131
135 std::meta::info type{};
136 bool ambiguous{false};
137};
138
148consteval scanned_trampoline scanned_trampoline_of(std::meta::info base) {
149 scanned_trampoline result{};
150 for (auto mem : std::meta::members_of(std::meta::parent_of(base),
151 std::meta::access_context::current())) {
152 if (mem == base || !std::meta::is_type(mem) || !is_trampoline(mem))
153 continue;
154 for (auto b :
155 std::meta::bases_of(mem, std::meta::access_context::current())) {
156 if (std::meta::dealias(std::meta::type_of(b)) ==
157 std::meta::dealias(base)) {
158 if (result.type != std::meta::info{})
159 result.ambiguous = true;
160 else
161 result.type = mem;
162 break;
163 }
164 }
165 }
166 return result;
167}
168
169// --- virtual-method reflection ----------------------------------------------
170
180consteval bool is_overridable_virtual(std::meta::info member) {
181 return std::meta::is_function(member) && std::meta::is_virtual(member) &&
182 !std::meta::is_destructor(member) && !bound_flat(member);
183}
184
185namespace detail {
186
196consteval bool same_slot(std::meta::info a, std::meta::info b) {
197 if (std::meta::identifier_of(a) != std::meta::identifier_of(b))
198 return false;
199 if (std::meta::is_const(a) != std::meta::is_const(b) ||
200 std::meta::is_volatile(a) != std::meta::is_volatile(b) ||
201 std::meta::is_lvalue_reference_qualified(a) !=
202 std::meta::is_lvalue_reference_qualified(b) ||
203 std::meta::is_rvalue_reference_qualified(a) !=
204 std::meta::is_rvalue_reference_qualified(b))
205 return false;
206 auto pa{std::meta::parameters_of(a)};
207 auto pb{std::meta::parameters_of(b)};
208 if (pa.size() != pb.size())
209 return false;
210 for (std::size_t i{0}; i < pa.size(); ++i)
211 if (std::meta::type_of(pa[i]) != std::meta::type_of(pb[i]))
212 return false;
213 return true;
214}
215
237consteval void collect_virtuals(std::meta::info type,
238 std::vector<std::meta::info>& slots) {
239 for (auto m :
240 std::meta::members_of(type, std::meta::access_context::unchecked())) {
241 if (!std::meta::is_function(m) || !std::meta::is_virtual(m) ||
242 std::meta::is_destructor(m) || !std::meta::has_identifier(m))
243 continue;
244 bool seen{false};
245 for (auto s : slots)
246 if (same_slot(s, m)) {
247 seen = true;
248 break;
249 }
250 if (!seen)
251 slots.push_back(m);
252 }
253 for (auto b :
254 std::meta::bases_of(type, std::meta::access_context::unchecked()))
255 collect_virtuals(std::meta::dealias(std::meta::type_of(b)), slots);
256}
257
258} // namespace detail
259
276consteval std::vector<std::meta::info> overridable_virtuals(std::meta::info type) {
277 // Accept an alias reflection too (the spelling a generated trampoline uses for
278 // a class-template specialization): the walk needs the underlying class.
279 type = std::meta::dealias(type);
280 std::vector<std::meta::info> all{};
281 detail::collect_virtuals(type, all);
282 std::vector<std::meta::info> out{};
283 for (auto s : all)
284 if (!bound_flat(s) && !std::meta::is_private(s))
285 out.push_back(s);
286 return out;
287}
288
315consteval std::meta::info virtual_slot(std::meta::info type, std::string_view name,
316 std::meta::info fn_type) {
317 for (auto s : overridable_virtuals(type))
318 if (std::meta::identifier_of(s) == name &&
319 std::meta::type_of(s) == std::meta::dealias(fn_type))
320 return s;
321 throw ::welder::diag::no_matching_virtual_slot{};
322}
323
334consteval std::size_t virtual_slot_count(std::meta::info type) {
335 return overridable_virtuals(type).size();
336}
337
341consteval bool has_virtual_methods(std::meta::info type) {
342 return !overridable_virtuals(type).empty();
343}
344
358template <class T>
359consteval std::meta::info construction_type_of() {
360 if (trampoline_for<T> != std::meta::info{})
361 return trampoline_for<T>;
362 // Only a virtual type can carry a trampoline — skip the namespace scan otherwise.
363 if (has_virtual_methods(^^T))
364 if (auto scanned{scanned_trampoline_of(^^T)};
365 scanned.type != std::meta::info{})
366 return scanned.type;
367 return ^^T;
368}
369
386consteval bool declares_override(std::meta::info tramp, std::meta::info vfn) {
387 auto name{std::meta::identifier_of(vfn)};
388 auto sig{std::meta::type_of(vfn)};
389 for (auto m :
390 std::meta::members_of(tramp, std::meta::access_context::unchecked())) {
391 if (std::meta::is_function(m) && !std::meta::is_special_member_function(m) &&
392 std::meta::identifier_of(m) == name && std::meta::type_of(m) == sig) {
393 return true;
394 }
395 }
396 return false;
397}
398
410consteval bool trampoline_covers(std::meta::info type, std::meta::info tramp) {
411 for (auto m : overridable_virtuals(type))
412 if (!declares_override(tramp, m))
413 return false;
414 return true;
415}
416
417} // namespace welder::rods::python
welder's consteval diagnostics, collected in one place: every hand-rolled compile-time error the libr...
The stored forms of the annotation vocabulary.
consteval void collect_virtuals(std::meta::info type, std::vector< std::meta::info > &slots)
Accumulate the most-derived declaration of every virtual member function reachable in type's complete...
consteval bool same_slot(std::meta::info a, std::meta::info b)
Do a and b declare the same vtable slot?
consteval bool is_overridable_virtual(std::meta::info member)
Is member an overridable virtual — a virtual member function that welder routes through the trampolin...
consteval std::vector< std::meta::info > overridable_virtuals(std::meta::info type)
Every overridable virtual slot of type — the ones welder routes through a trampoline — folding in vir...
consteval std::meta::info virtual_slot(std::meta::info type, std::string_view name, std::meta::info fn_type)
The overridable virtual slot of type named name whose function type is fn_type — the hand-written dis...
consteval bool is_trampoline(std::meta::info type)
Does type carry a trampoline mark?
consteval bool has_virtual_methods(std::meta::info type)
Does type declare or inherit any overridable virtual method?
consteval scanned_trampoline scanned_trampoline_of(std::meta::info base)
Find the trampoline-annotated class deriving directly from base by scanning base's enclosing namespac...
consteval std::size_t virtual_slot_count(std::meta::info type)
The number of overridable virtual member functions of type, inherited ones included.
consteval bool trampoline_covers(std::meta::info type, std::meta::info tramp)
Does tramp override every virtual method of type — inherited ones included?
constexpr bind_flat_spec bind_flat
Mark a virtual entity as deliberately bound non-overridably.
constexpr trampoline_spec trampoline
Mark a class as the trampoline for the base it derives from — the annotation form of trampoline_for.
consteval bool declares_override(std::meta::info tramp, std::meta::info vfn)
Does trampoline declare an override for the virtual method vfn?
consteval std::meta::info construction_type_of()
The type welder constructs when binding T: its registered/annotated trampoline if one exists,...
constexpr std::meta::info trampoline_for
The trampoline subclass registered for T, or a null reflection if none.
consteval bool bound_flat(std::meta::info entity)
Does entity (a type or a member function) carry a bind_flat mark?
The stored form of a bind_flat mark (a plain tag — it carries no state).
The outcome of scanning a base's namespace for its trampoline-annotated subclass.
bool ambiguous
true iff more than one candidate was found.
std::meta::info type
the trampoline, or a null reflection if none found.
The stored form of a trampoline mark (a plain tag — it carries no state).
welder's annotation vocabulary — the single header a consuming TU includes to get the markers it atta...