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 | 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 | |
662 | QT_END_NAMESPACE |
663 | |
664 |
Definitions
- RemoveRef
- RemoveRef
- RemoveConstRef
- RemoveConstRef
- List
- size
- SizeOfList
- value
- SizeOfList
- value
- SizeOfList
- value
- List
- size
- List_Append
- List_Left
- List_Left
- FunctorCallBase
- call_internal
- IndexesList
- ConcatSeqImpl
- GenSeq
- GenSeq
- GenSeq
- Indexes
- FunctionPointer
- assertObjectType
- FunctorCall
- call
- FunctorCall
- call
- FunctorCall
- call
- FunctorCall
- call
- FunctorCall
- call
- FunctionPointer
- call
- FunctionPointer
- call
- FunctionPointer
- call
- FunctionPointer
- call
- FunctionPointer
- call
- FunctionPointer
- call
- NarrowingDetector
- IsConvertibleWithoutNarrowing
- IsConvertibleWithoutNarrowing
- AreArgumentsConvertibleWithoutNarrowingBase
- AreArgumentsConvertibleWithoutNarrowingBase
- AreArgumentsCompatible
- AreArgumentsCompatible
- AreArgumentsCompatible
- AreArgumentsCompatible
- AreArgumentsCompatible
- AreArgumentsCompatible
- CheckCompatibleArguments
- CheckCompatibleArguments
- CheckCompatibleArguments
- CheckCompatibleArguments
- ComputeFunctorArgumentCountHelper
- ComputeFunctorArgumentCountHelper
- ComputeFunctorArgumentCount
- FunctorReturnType
- FunctorCallable
- call
- HasCallOperatorAcceptingArgs
- Test
- Test
- value
- HasCallOperatorAcceptingArgs_v
- CallableHelper
- Callable
- Callable
- countMatchingArguments
- QSlotObjectBase
- Operation
- QSlotObjectBase
- Deleter
- operator()
- cleanup
- ref
- destroyIfLastRef
- compare
- call
- isImpl
- ~QSlotObjectBase
- QSlotObjectBase
- copy
- SlotObjSharedPtr
- SlotObjSharedPtr
- SlotObjSharedPtr
- SlotObjSharedPtr
- SlotObjSharedPtr
- operator=
- SlotObjSharedPtr
- operator=
- ~SlotObjSharedPtr
- swap
- get
- operator->
- operator bool
- QCallableObject
- impl
- QCallableObject
- QCallableObject
- ContextTypeForFunctor
- ContextTypeForFunctor
- ContextTypeForFunctor
- makeCallableObject
- AreFunctionsCompatible
- AreFunctionsCompatible
Start learning QML with our Intro Training
Find out more