51 template <
typename Arg>
52 static constexpr bool passArgAsValue =
sizeof(
Arg) <=
sizeof(
size_t)
55 template <
typename C = Interface>
using Methods =
typename C::
template MethodTemplates<C>;
57 template <
typename ...>
60 template <
typename M,
typename R,
typename I,
typename... Args>
61 struct MethodImpl<M, R, I, Args...>
63 static_assert(
std::is_base_of_v<I, Interface>,
"The method must belong to the interface");
64 using return_type = R;
65 using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
67 static constexpr size_t index()
69 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
74 static constexpr bool matchesAt()
76 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
79 template <size_t... Is>
80 static constexpr size_t index(std::index_sequence<Is...>)
82 constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
83 static_assert(matchesCount == 1,
"Expected exactly one match");
84 return ((size_t(matchesAt<Is>()) * Is) + ...);
87 static R invoke(I &intf , Args... args)
89 Q_ASSERT(intf.m_callFN);
91 auto& baseIntf =
static_cast<
base_interface&>(
const_cast<std::remove_const_t<I>&>(intf));
92 call_args callArgs(
std::forward<Args>(args)...);
93 if constexpr (
std::is_void_v<R>) {
94 intf.m_callFN(index(), baseIntf,
nullptr, &callArgs);
96 alignas(R)
std::byte buf[
sizeof(R)];
97 intf.m_callFN(index(), baseIntf, buf, &callArgs);
99 R* result =
std::launder(
reinterpret_cast<R*>(buf));
100 QScopeGuard destroyBuffer([result]() {
std::destroy_at(result); });
101 return std::forward<R>(*result);
108 template <
typename M,
typename R,
typename I,
typename... Args>
109 struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
110 template <
typename Subclass>
111 using Overridden = R(Subclass::*)(Args...);
114 template <
typename M,
typename R,
typename I,
typename... Args>
115 struct MethodImpl<M, R(I::*)(Args...)
const> : MethodImpl<M, R,
const I, Args...> {
116 template <
typename Subclass>
117 using Overridden = R(Subclass::*)(Args...)
const;
121 template <
auto prototype>
124 template <
typename Method,
typename... Args>
125 auto call(Args &&... args)
const
127 return Method::invoke(
static_cast<
const Interface &>(*
this),
std::forward<Args>(args)...);
130 template <
typename Method,
typename... Args>
133 return Method::invoke(
static_cast<Interface &>(*
this),
std::forward<Args>(args)...);
161 template <
typename C = Subclass>
using Methods =
typename C::
template MethodTemplates<C>;
163 template <size_t OverriddenIndex>
164 static constexpr size_t interfaceMethodIndex() {
165 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
168 template <size_t... Is>
169 static void callImpl(size_t index, Subclass &subclass,
void *ret,
void *args, std::index_sequence<Is...>)
171 constexpr auto methodIndexMask = []() {
172 std::array<
bool,
sizeof...(Is)> result = {};
173 (
static_cast<
void>(
std::get<interfaceMethodIndex<Is>()>(result) =
true), ...);
176 static_assert((methodIndexMask[Is] && ...),
177 "Mapping between base and overridden methods is not unique");
179 auto doInvoke = [&](
auto idxConstant) {
180 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
182 QtPrivate::applyIndexSwitch(index, doInvoke,
183 std::index_sequence<interfaceMethodIndex<Is>()...>{});
186 static void callImpl(size_t index,
typename Interface::base_interface &intf,
void *ret,
void *args)
188 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
189 callImpl(index,
static_cast<Subclass&>(intf), ret, args, seq);
192 template <
typename BaseMethod>
193 using OverridenSignature =
typename BaseMethod::
template Overridden<Subclass>;
196 template <
typename... Args>
198 : Interface(
std::forward<Args>(args)...)
200 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
204 template <
typename BaseMethod, OverridenSignature<BaseMethod> overridden>
208 static constexpr void doInvoke(Subclass &subclass,
void *ret,
void *args)
210 using Return =
typename BaseMethod::return_type;
211 using PackedArgs =
typename BaseMethod::call_args;
214 Q_ASSERT(
std::is_void_v<Return> == !ret);
216 auto invoke = [&subclass](
auto &&...params)
218 return std::invoke(overridden, &subclass, std::forward<
decltype(params)>(params)...);
221 if constexpr (
std::is_void_v<Return>) {
222 std::apply(invoke,
std::move(*
static_cast<PackedArgs *>(args)));
224 q20::construct_at(
static_cast<Return *>(ret),
225 std::apply(invoke, std::move(*
static_cast<PackedArgs *>(args))));