50 template <
typename Arg>
51 static constexpr bool passArgAsValue =
sizeof(
Arg) <=
sizeof(
size_t)
54 template <
typename C = Interface>
using Methods =
typename C::
template MethodTemplates<C>;
56 template <
typename ...>
59 template <
typename M,
typename R,
typename I,
typename... Args>
60 struct MethodImpl<M, R, I, Args...>
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&&>...>;
66 static constexpr size_t index()
68 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
73 static constexpr bool matchesAt()
75 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
78 template <size_t... Is>
79 static constexpr size_t index(std::index_sequence<Is...>)
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) + ...);
86 static R invoke(I &intf , Args... args)
88 Q_ASSERT(intf.m_callFN);
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);
95 alignas(R)
std::byte buf[
sizeof(R)];
96 intf.m_callFN(index(), baseIntf, buf, &callArgs);
98 R* result =
std::launder(
reinterpret_cast<R*>(buf));
99 QScopeGuard destroyBuffer([result]() {
std::destroy_at(result); });
100 return std::forward<R>(*result);
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...);
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;
120 template <
auto prototype>
123 template <
typename Method,
typename... Args>
124 auto call(Args &&... args)
const
126 return Method::invoke(
static_cast<
const Interface &>(*
this),
std::forward<Args>(args)...);
129 template <
typename Method,
typename... Args>
132 return Method::invoke(
static_cast<Interface &>(*
this),
std::forward<Args>(args)...);
160 template <
typename C = Subclass>
using Methods =
typename C::
template MethodTemplates<C>;
162 template <size_t OverriddenIndex>
163 static constexpr size_t interfaceMethodIndex() {
164 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
167 template <size_t... Is>
168 static void callImpl(size_t index, Subclass &subclass,
void *ret,
void *args, std::index_sequence<Is...>)
170 constexpr auto methodIndexMask = []() {
171 std::array<
bool,
sizeof...(Is)> result = {};
172 (
static_cast<
void>(
std::get<interfaceMethodIndex<Is>()>(result) =
true), ...);
175 static_assert((methodIndexMask[Is] && ...),
176 "Mapping between base and overridden methods is not unique");
178 auto doInvoke = [&](
auto idxConstant) {
179 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
181 QtPrivate::applyIndexSwitch(index, doInvoke,
182 std::index_sequence<interfaceMethodIndex<Is>()...>{});
185 static void callImpl(size_t index,
typename Interface::base_interface &intf,
void *ret,
void *args)
187 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
188 callImpl(index,
static_cast<Subclass&>(intf), ret, args, seq);
191 template <
typename BaseMethod>
192 using OverridenSignature =
typename BaseMethod::
template Overridden<Subclass>;
195 template <
typename... Args>
197 : Interface(
std::forward<Args>(args)...)
199 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
203 template <
typename BaseMethod, OverridenSignature<BaseMethod> overridden>
207 static constexpr void doInvoke(Subclass &subclass,
void *ret,
void *args)
209 using Return =
typename BaseMethod::return_type;
210 using PackedArgs =
typename BaseMethod::call_args;
213 Q_ASSERT(
std::is_void_v<Return> == !ret);
215 auto invoke = [&subclass](
auto &&...params)
217 return std::invoke(overridden, &subclass, std::forward<
decltype(params)>(params)...);
220 if constexpr (
std::is_void_v<Return>) {
221 std::apply(invoke,
std::move(*
static_cast<PackedArgs *>(args)));
223 q20::construct_at(
static_cast<Return *>(ret),
224 std::apply(invoke, std::move(*
static_cast<PackedArgs *>(args))));