1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com> |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #ifndef QOBJECTDEFS_H |
6 | #error Do not include qobjectdefs_impl.h directly |
7 | #include <QtCore/qnamespace.h> |
8 | #endif |
9 | |
10 | #if 0 |
11 | #pragma qt_sync_skip_header_check |
12 | #pragma qt_sync_stop_processing |
13 | #endif |
14 | |
15 | #include <QtCore/qfunctionaltools_impl.h> |
16 | |
17 | #include <memory> |
18 | |
19 | QT_BEGIN_NAMESPACE |
20 | class QObject; |
21 | class QObjectPrivate; |
22 | class QMetaMethod; |
23 | class QByteArray; |
24 | |
25 | namespace QtPrivate { |
26 | template <typename T> struct RemoveRef { typedef T Type; }; |
27 | template <typename T> struct RemoveRef<T&> { typedef T Type; }; |
28 | template <typename T> struct RemoveConstRef { typedef T Type; }; |
29 | template <typename T> struct RemoveConstRef<const T&> { typedef T Type; }; |
30 | |
31 | /* |
32 | The following List classes are used to help to handle the list of arguments. |
33 | It follow the same principles as the lisp lists. |
34 | List_Left<L,N> take a list and a number as a parameter and returns (via the Value typedef, |
35 | the list composed of the first N element of the list |
36 | */ |
37 | // With variadic template, lists are represented using a variadic template argument instead of the lisp way |
38 | template <typename... Ts> struct List { static constexpr size_t size = sizeof...(Ts); }; |
39 | template<typename T> struct SizeOfList { static constexpr size_t value = 1; }; |
40 | template<> struct SizeOfList<List<>> { static constexpr size_t value = 0; }; |
41 | template<typename ...Ts> struct SizeOfList<List<Ts...>> { static constexpr size_t value = List<Ts...>::size; }; |
42 | template <typename Head, typename... Tail> struct List<Head, Tail...> { |
43 | static constexpr size_t size = 1 + sizeof...(Tail); |
44 | typedef Head Car; typedef List<Tail...> Cdr; |
45 | }; |
46 | template <typename, typename> struct List_Append; |
47 | template <typename... L1, typename...L2> struct List_Append<List<L1...>, List<L2...>> { typedef List<L1..., L2...> Value; }; |
48 | template <typename L, int N> struct List_Left { |
49 | typedef typename List_Append<List<typename L::Car>,typename List_Left<typename L::Cdr, N - 1>::Value>::Value Value; |
50 | }; |
51 | template <typename L> struct List_Left<L, 0> { typedef List<> Value; }; |
52 | |
53 | /* |
54 | Trick to set the return value of a slot that works even if the signal or the slot returns void |
55 | to be used like |
56 | function(), ApplyReturnValue<ReturnType>(&return_value) |
57 | if function() returns a value, the operator,(T, ApplyReturnValue<ReturnType>) is called, but if it |
58 | returns void, the built-in one is used without an error. |
59 | */ |
60 | template <typename T> |
61 | struct ApplyReturnValue { |
62 | void *data; |
63 | explicit ApplyReturnValue(void *data_) : data(data_) {} |
64 | }; |
65 | template<typename T, typename U> |
66 | void operator,(T &&value, const ApplyReturnValue<U> &container) { |
67 | if (container.data) |
68 | *reinterpret_cast<U *>(container.data) = std::forward<T>(value); |
69 | } |
70 | template<typename T> |
71 | void operator,(T, const ApplyReturnValue<void> &) {} |
72 | |
73 | |
74 | /* |
75 | The FunctionPointer<Func> struct is a type trait for function pointer. |
76 | - ArgumentCount is the number of argument, or -1 if it is unknown |
77 | - the Object typedef is the Object of a pointer to member function |
78 | - the Arguments typedef is the list of argument (in a QtPrivate::List) |
79 | - the Function typedef is an alias to the template parameter Func |
80 | - the call<Args, R>(f,o,args) method is used to call that slot |
81 | Args is the list of argument of the signal |
82 | R is the return type of the signal |
83 | f is the function pointer |
84 | o is the receiver object |
85 | and args is the array of pointer to arguments, as used in qt_metacall |
86 | |
87 | The Functor<Func,N> struct is the helper to call a functor of N argument. |
88 | Its call function is the same as the FunctionPointer::call function. |
89 | */ |
90 | template<class T> using InvokeGenSeq = typename T::Type; |
91 | |
92 | template<int...> struct IndexesList { using Type = IndexesList; }; |
93 | |
94 | template<int N, class S1, class S2> struct ConcatSeqImpl; |
95 | |
96 | template<int N, int... I1, int... I2> |
97 | struct ConcatSeqImpl<N, IndexesList<I1...>, IndexesList<I2...>> |
98 | : IndexesList<I1..., (N + I2)...>{}; |
99 | |
100 | template<int N, class S1, class S2> |
101 | using ConcatSeq = InvokeGenSeq<ConcatSeqImpl<N, S1, S2>>; |
102 | |
103 | template<int N> struct GenSeq; |
104 | template<int N> using makeIndexSequence = InvokeGenSeq<GenSeq<N>>; |
105 | |
106 | template<int N> |
107 | struct GenSeq : ConcatSeq<N/2, makeIndexSequence<N/2>, makeIndexSequence<N - N/2>>{}; |
108 | |
109 | template<> struct GenSeq<0> : IndexesList<>{}; |
110 | template<> struct GenSeq<1> : IndexesList<0>{}; |
111 | |
112 | template<int N> |
113 | struct Indexes { using Value = makeIndexSequence<N>; }; |
114 | |
115 | template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; }; |
116 | |
117 | template<typename ObjPrivate> inline void assertObjectType(QObjectPrivate *d); |
118 | template<typename Obj> inline void assertObjectType(QObject *o) |
119 | { |
120 | // ensure all three compile |
121 | [[maybe_unused]] auto staticcast = [](QObject *obj) { return static_cast<Obj *>(obj); }; |
122 | [[maybe_unused]] auto qobjcast = [](QObject *obj) { return Obj::staticMetaObject.cast(obj); }; |
123 | #ifdef __cpp_rtti |
124 | [[maybe_unused]] auto dyncast = [](QObject *obj) { return dynamic_cast<Obj *>(obj); }; |
125 | auto cast = dyncast; |
126 | #else |
127 | auto cast = qobjcast; |
128 | #endif |
129 | Q_ASSERT_X(cast(o), Obj::staticMetaObject.className(), |
130 | "Called object is not of the correct type (class destructor may have already run)" ); |
131 | } |
132 | |
133 | template <typename, typename, typename, typename> struct FunctorCall; |
134 | template <int... II, typename... SignalArgs, typename R, typename Function> |
135 | struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> { |
136 | static void call(Function &f, void **arg) { |
137 | f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); |
138 | } |
139 | }; |
140 | template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> |
141 | struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> { |
142 | static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) |
143 | { |
144 | assertObjectType<Obj>(o); |
145 | (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); |
146 | } |
147 | }; |
148 | template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> |
149 | struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const> { |
150 | static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg) |
151 | { |
152 | assertObjectType<Obj>(o); |
153 | (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); |
154 | } |
155 | }; |
156 | template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> |
157 | struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) noexcept> { |
158 | static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) |
159 | { |
160 | assertObjectType<Obj>(o); |
161 | (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); |
162 | } |
163 | }; |
164 | template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> |
165 | struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> { |
166 | static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) |
167 | { |
168 | assertObjectType<Obj>(o); |
169 | (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); |
170 | } |
171 | }; |
172 | |
173 | template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)> |
174 | { |
175 | typedef Obj Object; |
176 | typedef List<Args...> Arguments; |
177 | typedef Ret ReturnType; |
178 | typedef Ret (Obj::*Function) (Args...); |
179 | enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; |
180 | template <typename SignalArgs, typename R> |
181 | static void call(Function f, Obj *o, void **arg) { |
182 | FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); |
183 | } |
184 | }; |
185 | template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const> |
186 | { |
187 | typedef Obj Object; |
188 | typedef List<Args...> Arguments; |
189 | typedef Ret ReturnType; |
190 | typedef Ret (Obj::*Function) (Args...) const; |
191 | enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; |
192 | template <typename SignalArgs, typename R> |
193 | static void call(Function f, Obj *o, void **arg) { |
194 | FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); |
195 | } |
196 | }; |
197 | |
198 | template<typename Ret, typename... Args> struct FunctionPointer<Ret (*) (Args...)> |
199 | { |
200 | typedef List<Args...> Arguments; |
201 | typedef Ret ReturnType; |
202 | typedef Ret (*Function) (Args...); |
203 | enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false}; |
204 | template <typename SignalArgs, typename R> |
205 | static void call(Function f, void *, void **arg) { |
206 | FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, arg); |
207 | } |
208 | }; |
209 | |
210 | template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) noexcept> |
211 | { |
212 | typedef Obj Object; |
213 | typedef List<Args...> Arguments; |
214 | typedef Ret ReturnType; |
215 | typedef Ret (Obj::*Function) (Args...) noexcept; |
216 | enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; |
217 | template <typename SignalArgs, typename R> |
218 | static void call(Function f, Obj *o, void **arg) { |
219 | FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); |
220 | } |
221 | }; |
222 | template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const noexcept> |
223 | { |
224 | typedef Obj Object; |
225 | typedef List<Args...> Arguments; |
226 | typedef Ret ReturnType; |
227 | typedef Ret (Obj::*Function) (Args...) const noexcept; |
228 | enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; |
229 | template <typename SignalArgs, typename R> |
230 | static void call(Function f, Obj *o, void **arg) { |
231 | FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); |
232 | } |
233 | }; |
234 | |
235 | template<typename Ret, typename... Args> struct FunctionPointer<Ret (*) (Args...) noexcept> |
236 | { |
237 | typedef List<Args...> Arguments; |
238 | typedef Ret ReturnType; |
239 | typedef Ret (*Function) (Args...) noexcept; |
240 | enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false}; |
241 | template <typename SignalArgs, typename R> |
242 | static void call(Function f, void *, void **arg) { |
243 | FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, arg); |
244 | } |
245 | }; |
246 | |
247 | // Traits to detect if there is a conversion between two types, |
248 | // and that conversion does not include a narrowing conversion. |
249 | template <typename T> |
250 | struct NarrowingDetector { T t[1]; }; // from P0608 |
251 | |
252 | template <typename From, typename To, typename Enable = void> |
253 | struct IsConvertibleWithoutNarrowing : std::false_type {}; |
254 | |
255 | template <typename From, typename To> |
256 | struct IsConvertibleWithoutNarrowing<From, To, |
257 | std::void_t< decltype( NarrowingDetector<To>{ {std::declval<From>()} } ) > |
258 | > : std::true_type {}; |
259 | |
260 | // Check for the actual arguments. If they are exactly the same, |
261 | // then don't bother checking for narrowing; as a by-product, |
262 | // this solves the problem of incomplete types (which must be supported, |
263 | // or they would error out in the trait above). |
264 | template <typename From, typename To, typename Enable = void> |
265 | struct AreArgumentsConvertibleWithoutNarrowingBase : std::false_type {}; |
266 | |
267 | template <typename From, typename To> |
268 | struct AreArgumentsConvertibleWithoutNarrowingBase<From, To, |
269 | std::enable_if_t< |
270 | std::disjunction_v<std::is_same<From, To>, IsConvertibleWithoutNarrowing<From, To>> |
271 | > |
272 | > : std::true_type {}; |
273 | |
274 | /* |
275 | Logic that check if the arguments of the slot matches the argument of the signal. |
276 | To be used like this: |
277 | static_assert(CheckCompatibleArguments<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::value) |
278 | */ |
279 | template<typename A1, typename A2> struct AreArgumentsCompatible { |
280 | static int test(const std::remove_reference_t<A2>&); |
281 | static char test(...); |
282 | enum { value = sizeof(test(std::declval<std::remove_reference_t<A1>>())) == sizeof(int) }; |
283 | #ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT |
284 | using AreArgumentsConvertibleWithoutNarrowing = AreArgumentsConvertibleWithoutNarrowingBase<std::decay_t<A1>, std::decay_t<A2>>; |
285 | static_assert(AreArgumentsConvertibleWithoutNarrowing::value, "Signal and slot arguments are not compatible (narrowing)" ); |
286 | #endif |
287 | }; |
288 | template<typename A1, typename A2> struct AreArgumentsCompatible<A1, A2&> { enum { value = false }; }; |
289 | template<typename A> struct AreArgumentsCompatible<A&, A&> { enum { value = true }; }; |
290 | // void as a return value |
291 | template<typename A> struct AreArgumentsCompatible<void, A> { enum { value = true }; }; |
292 | template<typename A> struct AreArgumentsCompatible<A, void> { enum { value = true }; }; |
293 | template<> struct AreArgumentsCompatible<void, void> { enum { value = true }; }; |
294 | |
295 | template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; }; |
296 | template <> struct CheckCompatibleArguments<List<>, List<>> { enum { value = true }; }; |
297 | template <typename List1> struct CheckCompatibleArguments<List1, List<>> { enum { value = true }; }; |
298 | template <typename Arg1, typename Arg2, typename... Tail1, typename... Tail2> |
299 | struct CheckCompatibleArguments<List<Arg1, Tail1...>, List<Arg2, Tail2...>> |
300 | { |
301 | enum { value = AreArgumentsCompatible<typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value |
302 | && CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>::value }; |
303 | }; |
304 | |
305 | /* |
306 | Find the maximum number of arguments a functor object can take and be still compatible with |
307 | the arguments from the signal. |
308 | Value is the number of arguments, or -1 if nothing matches. |
309 | */ |
310 | template <typename Functor, typename ArgList> struct ComputeFunctorArgumentCount; |
311 | |
312 | template <typename Functor, typename ArgList, bool Done> struct ComputeFunctorArgumentCountHelper |
313 | { enum { Value = -1 }; }; |
314 | template <typename Functor, typename First, typename... ArgList> |
315 | struct ComputeFunctorArgumentCountHelper<Functor, List<First, ArgList...>, false> |
316 | : ComputeFunctorArgumentCount<Functor, |
317 | typename List_Left<List<First, ArgList...>, sizeof...(ArgList)>::Value> {}; |
318 | |
319 | template <typename Functor, typename... ArgList> struct ComputeFunctorArgumentCount<Functor, List<ArgList...>> |
320 | { |
321 | template <typename F> static auto test(F f) -> decltype(((f.operator()((std::declval<ArgList>())...)), int())); |
322 | static char test(...); |
323 | enum { |
324 | Ok = sizeof(test(std::declval<Functor>())) == sizeof(int), |
325 | Value = Ok ? int(sizeof...(ArgList)) : int(ComputeFunctorArgumentCountHelper<Functor, List<ArgList...>, Ok>::Value) |
326 | }; |
327 | }; |
328 | |
329 | /* get the return type of a functor, given the signal argument list */ |
330 | template <typename Functor, typename ArgList> struct FunctorReturnType; |
331 | template <typename Functor, typename ... ArgList> struct FunctorReturnType<Functor, List<ArgList...>> { |
332 | typedef decltype(std::declval<Functor>().operator()((std::declval<ArgList>())...)) Value; |
333 | }; |
334 | |
335 | template<typename Function, int N> struct Functor |
336 | { |
337 | template <typename SignalArgs, typename R> |
338 | static void call(Function &f, void *, void **arg) { |
339 | FunctorCall<typename Indexes<N>::Value, SignalArgs, R, Function>::call(f, arg); |
340 | } |
341 | }; |
342 | |
343 | template<typename Func> |
344 | struct ZeroArgFunctor : Functor<Func, 0> |
345 | { |
346 | using ReturnType = decltype(std::declval<Func>()()); |
347 | using Function = ReturnType(*)(); |
348 | enum {ArgumentCount = 0}; |
349 | using Arguments = QtPrivate::List<>; |
350 | }; |
351 | |
352 | template<typename Func> |
353 | using Callable = std::conditional_t<FunctionPointer<std::decay_t<Func>>::ArgumentCount == -1, |
354 | ZeroArgFunctor<std::decay_t<Func>>, |
355 | FunctionPointer<std::decay_t<Func>> |
356 | >; |
357 | |
358 | /* |
359 | Wrapper around ComputeFunctorArgumentCount and CheckCompatibleArgument, |
360 | depending on whether \a Functor is a PMF or not. Returns -1 if \a Func is |
361 | not compatible with the \a ExpectedArguments, otherwise returns >= 0. |
362 | */ |
363 | template<typename Prototype, typename Functor> |
364 | inline constexpr std::enable_if_t<!std::disjunction_v<std::is_convertible<Prototype, const char *>, |
365 | std::is_same<std::decay_t<Prototype>, QMetaMethod>, |
366 | std::is_convertible<Functor, const char *>, |
367 | std::is_same<std::decay_t<Functor>, QMetaMethod> |
368 | >, |
369 | int> |
370 | countMatchingArguments() |
371 | { |
372 | using ExpectedArguments = typename QtPrivate::FunctionPointer<Prototype>::Arguments; |
373 | using Actual = std::decay_t<Functor>; |
374 | |
375 | if constexpr (QtPrivate::FunctionPointer<Actual>::IsPointerToMemberFunction |
376 | || QtPrivate::FunctionPointer<Actual>::ArgumentCount >= 0) { |
377 | // PMF or free function |
378 | using ActualArguments = typename QtPrivate::FunctionPointer<Actual>::Arguments; |
379 | if constexpr (QtPrivate::CheckCompatibleArguments<ExpectedArguments, ActualArguments>::value) |
380 | return QtPrivate::FunctionPointer<Actual>::ArgumentCount; |
381 | else |
382 | return -1; |
383 | } else { |
384 | // lambda or functor |
385 | return QtPrivate::ComputeFunctorArgumentCount<Actual, ExpectedArguments>::Value; |
386 | } |
387 | } |
388 | |
389 | // internal base class (interface) containing functions required to call a slot managed by a pointer to function. |
390 | class QSlotObjectBase |
391 | { |
392 | // Don't use virtual functions here; we don't want the |
393 | // compiler to create tons of per-polymorphic-class stuff that |
394 | // we'll never need. We just use one function pointer, and the |
395 | // Operations enum below to distinguish requests |
396 | #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) |
397 | QAtomicInt m_ref = 1; |
398 | typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret); |
399 | const ImplFn m_impl; |
400 | #else |
401 | using ImplFn = void (*)(QSlotObjectBase* this_, QObject *receiver, void **args, int which, bool *ret); |
402 | const ImplFn m_impl; |
403 | QAtomicInt m_ref = 1; |
404 | #endif |
405 | protected: |
406 | // The operations that can be requested by calls to m_impl, |
407 | // see the member functions that call m_impl below for details |
408 | enum Operation { |
409 | Destroy, |
410 | Call, |
411 | Compare, |
412 | |
413 | NumOperations |
414 | }; |
415 | public: |
416 | explicit QSlotObjectBase(ImplFn fn) : m_impl(fn) {} |
417 | |
418 | // A custom deleter compatible with std protocols (op()()) we well as |
419 | // the legacy QScopedPointer protocol (cleanup()). |
420 | struct Deleter { |
421 | void operator()(QSlotObjectBase *p) const noexcept |
422 | { if (p) p->destroyIfLastRef(); } |
423 | // for the non-standard QScopedPointer protocol: |
424 | static void cleanup(QSlotObjectBase *p) noexcept { Deleter{}(p); } |
425 | }; |
426 | |
427 | bool ref() noexcept { return m_ref.ref(); } |
428 | #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) |
429 | inline void destroyIfLastRef() noexcept |
430 | { if (!m_ref.deref()) m_impl(Destroy, this, nullptr, nullptr, nullptr); } |
431 | |
432 | inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, nullptr, a, &ret); return ret; } |
433 | inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); } |
434 | #else |
435 | inline void destroyIfLastRef() noexcept |
436 | { if (!m_ref.deref()) m_impl(this, nullptr, nullptr, Destroy, nullptr); } |
437 | |
438 | inline bool compare(void **a) |
439 | { |
440 | bool ret = false; |
441 | m_impl(this, nullptr, a, Compare, &ret); |
442 | return ret; |
443 | } |
444 | inline void call(QObject *r, void **a) { m_impl(this, r, a, Call, nullptr); } |
445 | #endif |
446 | bool isImpl(ImplFn f) const { return m_impl == f; } |
447 | protected: |
448 | ~QSlotObjectBase() {} |
449 | private: |
450 | Q_DISABLE_COPY_MOVE(QSlotObjectBase) |
451 | }; |
452 | |
453 | using SlotObjUniquePtr = std::unique_ptr<QSlotObjectBase, |
454 | QSlotObjectBase::Deleter>; |
455 | inline SlotObjUniquePtr copy(const SlotObjUniquePtr &other) noexcept |
456 | { |
457 | if (other) |
458 | other->ref(); |
459 | return SlotObjUniquePtr{other.get()}; |
460 | } |
461 | |
462 | class SlotObjSharedPtr { |
463 | SlotObjUniquePtr obj; |
464 | public: |
465 | Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr() noexcept = default; |
466 | Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr(std::nullptr_t) noexcept : SlotObjSharedPtr() {} |
467 | Q_NODISCARD_CTOR explicit SlotObjSharedPtr(SlotObjUniquePtr o) |
468 | : obj(std::move(o)) |
469 | { |
470 | // does NOT ref() (takes unique_ptr by value) |
471 | // (that's why (QSlotObjectBase*) ctor doesn't exisit: don't know whether that one _should_) |
472 | } |
473 | Q_NODISCARD_CTOR SlotObjSharedPtr(const SlotObjSharedPtr &other) noexcept |
474 | : obj{copy(other: other.obj)} {} |
475 | SlotObjSharedPtr &operator=(const SlotObjSharedPtr &other) noexcept |
476 | { auto copy = other; swap(other&: copy); return *this; } |
477 | |
478 | Q_NODISCARD_CTOR SlotObjSharedPtr(SlotObjSharedPtr &&other) noexcept = default; |
479 | SlotObjSharedPtr &operator=(SlotObjSharedPtr &&other) noexcept = default; |
480 | ~SlotObjSharedPtr() = default; |
481 | |
482 | void swap(SlotObjSharedPtr &other) noexcept { obj.swap(u&: other.obj); } |
483 | |
484 | auto get() const noexcept { return obj.get(); } |
485 | auto operator->() const noexcept { return get(); } |
486 | |
487 | explicit operator bool() const noexcept { return bool(obj); } |
488 | }; |
489 | |
490 | |
491 | // Implementation of QSlotObjectBase for which the slot is a callable (function, PMF, functor, or lambda). |
492 | // Args and R are the List of arguments and the return type of the signal to which the slot is connected. |
493 | template <typename Func, typename Args, typename R> |
494 | class QCallableObject : public QSlotObjectBase, |
495 | private QtPrivate::CompactStorage<std::decay_t<Func>> |
496 | { |
497 | using FunctorValue = std::decay_t<Func>; |
498 | using Storage = QtPrivate::CompactStorage<FunctorValue>; |
499 | using FuncType = std::conditional_t<std::is_member_function_pointer_v<FunctorValue>, |
500 | QtPrivate::FunctionPointer<FunctorValue>, |
501 | QtPrivate::Functor<FunctorValue, Args::size> |
502 | >; |
503 | |
504 | #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) |
505 | Q_DECL_HIDDEN static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) |
506 | #else |
507 | // Design note: the first three arguments match those for typical Call |
508 | // and Destroy uses. We return void to enable tail call optimization |
509 | // for those too. |
510 | Q_DECL_HIDDEN static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret) |
511 | #endif |
512 | { |
513 | const auto that = static_cast<QCallableObject*>(this_); |
514 | switch (which) { |
515 | case Destroy: |
516 | delete that; |
517 | break; |
518 | case Call: |
519 | if constexpr (std::is_member_function_pointer_v<FunctorValue>) |
520 | FuncType::template call<Args, R>(that->object(), static_cast<typename FuncType::Object *>(r), a); |
521 | else |
522 | FuncType::template call<Args, R>(that->object(), r, a); |
523 | break; |
524 | case Compare: |
525 | if constexpr (std::is_member_function_pointer_v<FunctorValue>) { |
526 | *ret = *reinterpret_cast<FunctorValue *>(a) == that->object(); |
527 | break; |
528 | } |
529 | // not implemented otherwise |
530 | Q_FALLTHROUGH(); |
531 | case NumOperations: |
532 | Q_UNUSED(ret); |
533 | } |
534 | } |
535 | public: |
536 | explicit QCallableObject(Func &&f) : QSlotObjectBase(&impl), Storage{std::move(f)} {} |
537 | explicit QCallableObject(const Func &f) : QSlotObjectBase(&impl), Storage{f} {} |
538 | }; |
539 | |
540 | // Helper to detect the context object type based on the functor type: |
541 | // QObject for free functions and lambdas; the callee for member function |
542 | // pointers. The default declaration doesn't have the ContextType typedef, |
543 | // and so non-functor APIs (like old-style string-based slots) are removed |
544 | // from the overload set. |
545 | template <typename Func, typename = void> |
546 | struct ContextTypeForFunctor {}; |
547 | |
548 | template <typename Func> |
549 | struct ContextTypeForFunctor<Func, |
550 | std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>, |
551 | std::is_member_function_pointer<Func> |
552 | > |
553 | > |
554 | > |
555 | { |
556 | using ContextType = QObject; |
557 | }; |
558 | template <typename Func> |
559 | struct ContextTypeForFunctor<Func, |
560 | std::enable_if_t<std::conjunction_v<std::negation<std::is_convertible<Func, const char *>>, |
561 | std::is_member_function_pointer<Func>, |
562 | std::is_convertible<typename QtPrivate::FunctionPointer<Func>::Object *, QObject *> |
563 | > |
564 | > |
565 | > |
566 | { |
567 | using ContextType = typename QtPrivate::FunctionPointer<Func>::Object; |
568 | }; |
569 | |
570 | /* |
571 | Returns a suitable QSlotObjectBase object that holds \a func, if possible. |
572 | |
573 | Not available (and thus produces compile-time errors) if the Functor provided is |
574 | not compatible with the expected Prototype. |
575 | */ |
576 | template <typename Prototype, typename Functor> |
577 | static constexpr std::enable_if_t<QtPrivate::countMatchingArguments<Prototype, Functor>() >= 0, |
578 | QtPrivate::QSlotObjectBase *> |
579 | makeCallableObject(Functor &&func) |
580 | { |
581 | using ExpectedSignature = QtPrivate::FunctionPointer<Prototype>; |
582 | using ExpectedReturnType = typename ExpectedSignature::ReturnType; |
583 | using ExpectedArguments = typename ExpectedSignature::Arguments; |
584 | |
585 | using ActualSignature = QtPrivate::FunctionPointer<Functor>; |
586 | constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments<Prototype, Functor>(); |
587 | using ActualArguments = typename QtPrivate::List_Left<ExpectedArguments, MatchingArgumentCount>::Value; |
588 | |
589 | static_assert(int(ActualSignature::ArgumentCount) <= int(ExpectedSignature::ArgumentCount), |
590 | "Functor requires more arguments than what can be provided." ); |
591 | |
592 | return new QtPrivate::QCallableObject<std::decay_t<Functor>, ActualArguments, ExpectedReturnType>(std::forward<Functor>(func)); |
593 | } |
594 | |
595 | template<typename Prototype, typename Functor, typename = void> |
596 | struct AreFunctionsCompatible : std::false_type {}; |
597 | template<typename Prototype, typename Functor> |
598 | struct AreFunctionsCompatible<Prototype, Functor, std::enable_if_t< |
599 | std::is_same_v<decltype(QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(std::declval<Functor>()))), |
600 | QtPrivate::QSlotObjectBase *>> |
601 | > : std::true_type {}; |
602 | |
603 | template<typename Prototype, typename Functor> |
604 | inline constexpr bool AssertCompatibleFunctions() { |
605 | static_assert(AreFunctionsCompatible<Prototype, Functor>::value, |
606 | "Functor is not compatible with expected prototype!" ); |
607 | return true; |
608 | } |
609 | } |
610 | |
611 | QT_END_NAMESPACE |
612 | |
613 | |