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
19QT_BEGIN_NAMESPACE
20class QObject;
21class QObjectPrivate;
22class QMetaMethod;
23class QByteArray;
24
25namespace 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
611QT_END_NAMESPACE
612
613

source code of qtbase/src/corelib/kernel/qobjectdefs_impl.h