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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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