49 template <
typename Arg>
50 static constexpr bool passArgAsValue =
sizeof(
Arg) <=
sizeof(
size_t)
53 template <
typename ...>
56 template <
typename M,
typename R,
typename I,
typename... Args>
57 struct MethodImpl<M, R, I, Args...>
59 static_assert(
std::is_base_of_v<I, Interface>,
"The method must belong to the interface");
60 using return_type = R;
61 using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
63 static constexpr size_t index()
65 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
70 static constexpr bool matchesAt()
72 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
75 template <size_t... Is>
76 static constexpr size_t index(std::index_sequence<Is...>)
78 constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
79 static_assert(matchesCount == 1,
"Expected exactly one match");
80 return ((size_t(matchesAt<Is>()) * Is) + ...);
83 static R invoke(I &intf , Args... args)
85 Q_ASSERT(intf.m_callFN);
87 auto& baseIntf =
static_cast<
base_interface&>(
const_cast<std::remove_const_t<I>&>(intf));
88 call_args callArgs(
std::forward<Args>(args)...);
89 if constexpr (
std::is_void_v<R>) {
90 intf.m_callFN(index(), baseIntf,
nullptr, &callArgs);
92 alignas(R)
std::byte buf[
sizeof(R)];
93 intf.m_callFN(index(), baseIntf, buf, &callArgs);
95 R* result =
std::launder(
reinterpret_cast<R*>(buf));
96 QScopeGuard destroyBuffer([result]() {
std::destroy_at(result); });
97 return std::forward<R>(*result);
104 template <
typename M,
typename R,
typename I,
typename... Args>
105 struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
106 template <
typename Subclass>
107 using Overridden = R(Subclass::*)(Args...);
110 template <
typename M,
typename R,
typename I,
typename... Args>
111 struct MethodImpl<M, R(I::*)(Args...)
const> : MethodImpl<M, R,
const I, Args...> {
112 template <
typename Subclass>
113 using Overridden = R(Subclass::*)(Args...)
const;
116 template <
typename C = Interface>
using Methods =
typename C::
template MethodTemplates<C>;
119 template <
auto prototype>
122 template <
typename Method,
typename... Args>
123 auto call(Args &&... args)
const
125 return Method::invoke(
static_cast<
const Interface &>(*
this),
std::forward<Args>(args)...);
128 template <
typename Method,
typename... Args>
131 return Method::invoke(
static_cast<Interface &>(*
this),
std::forward<Args>(args)...);
159 template <
typename C = Subclass>
using Methods =
typename C::
template MethodTemplates<C>;
161 template <size_t OverriddenIndex>
162 static constexpr size_t interfaceMethodIndex() {
163 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
166 template <size_t... Is>
167 static void callImpl(size_t index, Subclass &subclass,
void *ret,
void *args, std::index_sequence<Is...>)
169 constexpr auto methodIndexMask = []() {
170 std::array<
bool,
sizeof...(Is)> result = {};
171 (
static_cast<
void>(
std::get<interfaceMethodIndex<Is>()>(result) =
true), ...);
174 static_assert((methodIndexMask[Is] && ...),
175 "Mapping between base and overridden methods is not unique");
177 auto doInvoke = [&](
auto idxConstant) {
178 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
180 applyIndexSwitch(index, doInvoke, std::index_sequence<interfaceMethodIndex<Is>()...>{});
183 static void callImpl(size_t index,
typename Interface::base_interface &intf,
void *ret,
void *args)
185 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
186 callImpl(index,
static_cast<Subclass&>(intf), ret, args, seq);
189 template <
typename BaseMethod>
190 using OverridenSignature =
typename BaseMethod::
template Overridden<Subclass>;
193 template <
typename... Args>
195 : Interface(
std::forward<Args>(args)...)
197 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
201 template <
typename BaseMethod, OverridenSignature<BaseMethod> overridden>
205 static constexpr void doInvoke(Subclass &subclass,
void *ret,
void *args)
207 using Return =
typename BaseMethod::return_type;
208 using PackedArgs =
typename BaseMethod::call_args;
211 Q_ASSERT(
std::is_void_v<Return> == !ret);
213 auto invoke = [&subclass](
auto &&...params)
215 return std::invoke(overridden, &subclass, std::forward<
decltype(params)>(params)...);
218 if constexpr (
std::is_void_v<Return>) {
219 std::apply(invoke,
std::move(*
static_cast<PackedArgs *>(args)));
223 using Alloc = std::allocator<Return>;
225 std::allocator_traits<Alloc>::construct(alloc,
static_cast<Return *>(ret),
226 std::apply(invoke, std::move(*
static_cast<PackedArgs *>(args))));