Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquasivirtual_impl.h
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QQUASIVIRTUAL_IMPL_H
6#define QQUASIVIRTUAL_IMPL_H
7
8#if 0
9#pragma qt_sync_skip_header_check
10#pragma qt_sync_stop_processing
11#endif
12
13#include <QtCore/qscopeguard.h>
14#include <QtCore/qassert.h>
15#include <QtCore/qtclasshelpermacros.h>
16
17#include <algorithm>
18#include <QtCore/q20memory.h>
19#include <type_traits>
20#include <tuple>
21
22#ifndef Q_QDOC
23
24QT_BEGIN_NAMESPACE
25
26namespace QtPrivate {
27
28template <typename Applier, size_t ...Is>
29void applyIndexSwitch(size_t index, Applier&& applier, std::index_sequence<Is...>)
30{
31 // Performance considerations:
32 // The folding expression used here represents the same logic as a sequence of
33 // linear if/else if/... statements. Experiments show that Clang, GCC, and MSVC
34 // optimize it to essentially the same bytecode as a normal C++ switch,
35 // ensuring O(1) lookup complexity.
36 static_cast<void>(((Is == index && (applier(std::integral_constant<size_t, Is>{}), true)) || ...));
37}
38
39template <size_t IndexCount, typename Applier>
40void applyIndexSwitch(size_t index, Applier&& applier)
41{
42 QtPrivate::applyIndexSwitch(index, std::forward<Applier>(applier),
43 std::make_index_sequence<IndexCount>());
44}
45
46template <typename Interface>
48{
49private:
50 template <typename Arg>
51 static constexpr bool passArgAsValue = sizeof(Arg) <= sizeof(size_t)
53
54 template <typename C = Interface> using Methods = typename C::template MethodTemplates<C>;
55
56 template <typename ...>
57 struct MethodImpl;
58
59 template <typename M, typename R, typename I, typename... Args>
60 struct MethodImpl<M, R, I, Args...>
61 {
62 static_assert(std::is_base_of_v<I, Interface>, "The method must belong to the interface");
63 using return_type = R;
64 using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
65
66 static constexpr size_t index()
67 {
68 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
69 }
70
71 private:
72 template <size_t Ix>
73 static constexpr bool matchesAt()
74 {
75 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
76 }
77
78 template <size_t... Is>
79 static constexpr size_t index(std::index_sequence<Is...>)
80 {
81 constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
82 static_assert(matchesCount == 1, "Expected exactly one match");
83 return ((size_t(matchesAt<Is>()) * Is) + ...);
84 }
85
86 static R invoke(I &intf /*const validation*/, Args... args)
87 {
88 Q_ASSERT(intf.m_callFN);
89
90 auto& baseIntf = static_cast<base_interface&>(const_cast<std::remove_const_t<I>&>(intf));
91 call_args callArgs(std::forward<Args>(args)...);
92 if constexpr (std::is_void_v<R>) {
93 intf.m_callFN(index(), baseIntf, nullptr, &callArgs);
94 } else {
95 alignas(R) std::byte buf[sizeof(R)];
96 intf.m_callFN(index(), baseIntf, buf, &callArgs);
97
98 R* result = std::launder(reinterpret_cast<R*>(buf));
99 QScopeGuard destroyBuffer([result]() { std::destroy_at(result); });
100 return std::forward<R>(*result);
101 }
102 }
103
104 friend class QQuasiVirtualInterface<Interface>;
105 };
106
107 template <typename M, typename R, typename I, typename... Args>
108 struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
109 template <typename Subclass>
110 using Overridden = R(Subclass::*)(Args...);
111 };
112
113 template <typename M, typename R, typename I, typename... Args>
114 struct MethodImpl<M, R(I::*)(Args...) const> : MethodImpl<M, R, const I, Args...> {
115 template <typename Subclass>
116 using Overridden = R(Subclass::*)(Args...) const;
117 };
118
119public:
120 template <auto prototype>
121 struct Method : MethodImpl<Method<prototype>, decltype(prototype)> {};
122
123 template <typename Method, typename... Args>
124 auto call(Args &&... args) const
125 {
126 return Method::invoke(static_cast<const Interface &>(*this), std::forward<Args>(args)...);
127 }
128
129 template <typename Method, typename... Args>
130 auto call(Args &&... args)
131 {
132 return Method::invoke(static_cast<Interface &>(*this), std::forward<Args>(args)...);
133 }
134
135 void destroy(); // quasi-virtual pure destructor
137
138 struct Deleter
139 {
140 void operator () (QQuasiVirtualInterface* self) const { self->template call<Destroy>(); }
141 };
142
143protected:
145 using CallFN = void (*)(size_t index, base_interface &intf, void *ret, void *args);
146 void initCallFN(CallFN func) { m_callFN = func; }
147
150
151private:
153 CallFN m_callFN = nullptr;
154};
155
156template <typename Subclass, typename Interface>
157class QQuasiVirtualSubclass : public Interface
158{
159private:
160 template <typename C = Subclass> using Methods = typename C::template MethodTemplates<C>;
161
162 template <size_t OverriddenIndex>
163 static constexpr size_t interfaceMethodIndex() {
164 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
165 }
166
167 template <size_t... Is>
168 static void callImpl(size_t index, Subclass &subclass, void *ret, void *args, std::index_sequence<Is...>)
169 {
170 constexpr auto methodIndexMask = []() {
171 std::array<bool, sizeof...(Is)> result = {};
172 (static_cast<void>(std::get<interfaceMethodIndex<Is>()>(result) = true), ...);
173 return result;
174 }();
175 static_assert((methodIndexMask[Is] && ...),
176 "Mapping between base and overridden methods is not unique");
177
178 auto doInvoke = [&](auto idxConstant) {
179 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
180 };
181 QtPrivate::applyIndexSwitch(index, doInvoke,
182 std::index_sequence<interfaceMethodIndex<Is>()...>{});
183 }
184
185 static void callImpl(size_t index, typename Interface::base_interface &intf, void *ret, void *args)
186 {
187 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
188 callImpl(index, static_cast<Subclass&>(intf), ret, args, seq);
189 }
190
191 template <typename BaseMethod>
192 using OverridenSignature = typename BaseMethod::template Overridden<Subclass>;
193
194protected:
195 template <typename... Args>
196 QQuasiVirtualSubclass(Args &&... args)
197 : Interface(std::forward<Args>(args)...)
198 {
199 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
200 }
201
202public:
203 template <typename BaseMethod, OverridenSignature<BaseMethod> overridden>
204 struct Override : BaseMethod
205 {
206 private:
207 static constexpr void doInvoke(Subclass &subclass, void *ret, void *args)
208 {
209 using Return = typename BaseMethod::return_type;
210 using PackedArgs = typename BaseMethod::call_args;
211
212 Q_ASSERT(args);
213 Q_ASSERT(std::is_void_v<Return> == !ret);
214
215 auto invoke = [&subclass](auto &&...params)
216 {
217 return std::invoke(overridden, &subclass, std::forward<decltype(params)>(params)...);
218 };
219
220 if constexpr (std::is_void_v<Return>) {
221 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args)));
222 } else {
223 q20::construct_at(static_cast<Return *>(ret),
224 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args))));
225 }
226 }
227
228 friend class QQuasiVirtualSubclass<Subclass, Interface>;
229 };
230};
231
232} // namespace QtPrivate
233
234QT_END_NAMESPACE
235
236#endif // Q_DOC
237
238#endif // QQUASIVIRTUAL_IMPL_H
auto call(Args &&... args) const
QQuasiVirtualInterface< Interface > base_interface
void(*)(size_t index, base_interface &intf, void *ret, void *args) CallFN
void applyIndexSwitch(size_t index, Applier &&applier, std::index_sequence< Is... >)
void applyIndexSwitch(size_t index, Applier &&applier)
void operator()(QQuasiVirtualInterface *self) const