QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskMetaFunction.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskMetaFunction.h"
7#include "QskInternalMacros.h"
8
9#include <qcoreapplication.h>
10#include <qobject.h>
11#include <qthread.h>
12
13#if QT_CONFIG(thread)
14#include <qsemaphore.h>
15#endif
16
17QSK_QT_PRIVATE_BEGIN
18#include <private/qobject_p.h>
19QSK_QT_PRIVATE_END
20
21namespace
22{
23 using FunctionCall = QskMetaFunction::FunctionCall;
24
25 // to have access to the private section of QSlotObjectBase
26 struct SlotObject
27 {
28 QAtomicInt ref;
29 FunctionCall::InvokeFunction invoke;
30 const int* parameterTypes;
31 };
32
33 static_assert( sizeof( SlotObject ) == sizeof( FunctionCall ),
34 "Bad cast: QskMetaFunction does not match" );
35}
36
37int QskMetaFunction::FunctionCall::typeInfo() const
38{
39 auto that = const_cast< FunctionCall* >( this );
40
41 int value;
42
43 reinterpret_cast< SlotObject* >( that )->invoke(
44 TypeInfo, that, nullptr, reinterpret_cast< void** >( &value ), nullptr );
45
46 return value;
47}
48
49int QskMetaFunction::FunctionCall::refCount() const
50{
51 auto that = const_cast< FunctionCall* >( this );
52 return reinterpret_cast< SlotObject* >( that )->ref.loadRelaxed();
53}
54
55QskMetaFunction::QskMetaFunction()
56 : m_functionCall( nullptr )
57{
58}
59
60QskMetaFunction::QskMetaFunction( FunctionCall* functionCall )
61 : m_functionCall( functionCall )
62{
63 if ( m_functionCall )
64 m_functionCall->ref();
65}
66
67QskMetaFunction::QskMetaFunction( const QskMetaFunction& other )
68 : m_functionCall( other.m_functionCall )
69{
70 if ( m_functionCall )
71 m_functionCall->ref();
72}
73
74QskMetaFunction::QskMetaFunction( QskMetaFunction&& other )
75 : m_functionCall( other.m_functionCall )
76{
77 other.m_functionCall = nullptr;
78}
79
80QskMetaFunction::~QskMetaFunction()
81{
82 if ( m_functionCall )
83 m_functionCall->destroyIfLastRef();
84}
85
86QskMetaFunction& QskMetaFunction::operator=( QskMetaFunction&& other )
87{
88 if ( m_functionCall != other.m_functionCall )
89 {
90 if ( m_functionCall )
91 m_functionCall->destroyIfLastRef();
92
93 m_functionCall = other.m_functionCall;
94 other.m_functionCall = nullptr;
95 }
96
97 return *this;
98}
99
100QskMetaFunction& QskMetaFunction::operator=( const QskMetaFunction& other )
101{
102 if ( m_functionCall != other.m_functionCall )
103 {
104 if ( m_functionCall )
105 m_functionCall->destroyIfLastRef();
106
107 m_functionCall = other.m_functionCall;
108
109 if ( m_functionCall )
110 m_functionCall->ref();
111 }
112
113 return *this;
114}
115
116bool QskMetaFunction::operator==( const QskMetaFunction& other ) const
117{
118 if ( m_functionCall == other.m_functionCall )
119 return true;
120
121 /*
122 There is no way to compmare functors/members without
123 std::type_info, what we don't want to use as it is
124 another template creating symbols.
125
126 So this implementation can't do much more than finding
127 out if one instance is a copy from another.
128 */
129
130 if ( m_functionCall && other.m_functionCall )
131 {
132 if ( m_functionCall->typeInfo() == StaticFunction &&
133 other.m_functionCall->typeInfo() == StaticFunction )
134 {
135 // only static functions can be compared
136 return m_functionCall->compare(
137 reinterpret_cast< void** >( other.m_functionCall ) );
138 }
139 }
140
141 return false;
142}
143
144int QskMetaFunction::returnType() const
145{
146 return QMetaType::Void; // TODO ...
147}
148
149size_t QskMetaFunction::parameterCount() const
150{
151 size_t count = 0;
152
153 if ( auto types = parameterTypes() )
154 {
155 while ( types[ count ] != QMetaType::UnknownType )
156 count++;
157 }
158
159 return count;
160}
161
162QskMetaFunction::Type QskMetaFunction::functionType() const
163{
164 if ( m_functionCall == nullptr )
165 return Invalid;
166
167 return static_cast< QskMetaFunction::Type >( m_functionCall->typeInfo() );
168}
169
170void QskMetaFunction::invoke( QObject* object,
171 void* argv[], Qt::ConnectionType connectionType )
172{
173#if 1
174 /*
175 Since Qt 5.10 we also have QMetaObject::invokeMethod
176 with functor based callbacks. TODO ...
177 */
178#endif
179
180 // code is not thread safe - TODO ...
181
182 if ( m_functionCall == nullptr )
183 return;
184
185 QPointer< QObject > receiver( object );
186
187 int invokeType = connectionType & 0x3;
188
189 if ( invokeType == Qt::AutoConnection )
190 {
191 invokeType = ( receiver && receiver->thread() != QThread::currentThread() )
192 ? Qt::QueuedConnection : Qt::DirectConnection;
193 }
194
195 switch ( invokeType )
196 {
197 case Qt::DirectConnection:
198 {
199 m_functionCall->call( receiver, argv );
200 break;
201 }
202 case Qt::BlockingQueuedConnection:
203 {
204 if ( receiver.isNull() ||
205 ( receiver->thread() == QThread::currentThread() ) )
206 {
207 // We would end up in a deadlock, better do nothing
208 return;
209 }
210
211#if QT_CONFIG(thread)
212 QSemaphore semaphore;
213
214 auto event = new QMetaCallEvent(
215 m_functionCall, nullptr, 0, argv, &semaphore );
216#else
217 auto event = new QMetaCallEvent(
218 m_functionCall, nullptr, 0, argv, nullptr );
219#endif
220
221 QCoreApplication::postEvent( receiver, event );
222
223#if QT_CONFIG(thread)
224 semaphore.acquire();
225#endif
226
227 break;
228 }
229 case Qt::QueuedConnection:
230 {
231 if ( receiver.isNull() )
232 return;
233
234 const auto argc = parameterCount() + 1; // return value + arguments
235
236 auto event = new QMetaCallEvent( m_functionCall, nullptr, 0, argc );
237
238 auto types = event->types();
239 auto arguments = event->args();
240
241#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
242 types[0] = QMetaType();
243 arguments[ 0 ] = nullptr;
244#else
245 types[0] = 0;
246 arguments[ 0 ] = nullptr;
247#endif
248
249 const int* parameterTypes = m_functionCall->parameterTypes();
250 for ( uint i = 1; i < argc; i++ )
251 {
252 if ( argv[ i ] == nullptr )
253 {
254 Q_ASSERT( arguments[ i ] != nullptr );
255 receiver = nullptr;
256 break;
257 }
258
259 const auto type = parameterTypes[i - 1];
260
261#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
262 types[ i ] = QMetaType( type );
263 arguments[ i ] = QMetaType( type ).create( argv[ i ] );
264#else
265 types[ i ] = type;
266 arguments[ i ] = QMetaType::create( type, argv[ i ] );
267#endif
268 }
269
270 if ( receiver )
271 QCoreApplication::postEvent( receiver, event );
272 else
273 delete event;
274
275 break;
276 }
277 }
278}
279
280#include "moc_QskMetaFunction.cpp"