1 #pragma once
2
3 // Please excuse this code. We are trying to do something that Qt really doesn't
4 // want us to do, and as such, this class is a mess. This code is not a role
5 // model. Do not look up to this code. Do not base your code on this code. In
6 // order to do what it does, it has made terrible sacrifices and has blood on its
7 // hands. Use it only when necessary.
8
9 #include <QSharedPointer>
10 #include <QMetaMethod>
11 #include <QDebug>
12 #include <QtGlobal>
13
14 template <class T, class B>
15 class QmlSharedPointer;
16
17 class QmlSharedPointerBase
18 : public QObject
19 , public QSharedPointer<QObject>
20 {
21 public:
QmlSharedPointerBase(X * ptr)22 template <typename X> explicit QmlSharedPointerBase(X *ptr)
23 : QSharedPointer<QObject>(ptr, &QObject::deleteLater)
24 {
25 }
26
QmlSharedPointerBase(X * ptr,Deleter d)27 template <typename X, typename Deleter> QmlSharedPointerBase(X *ptr, Deleter d)
28 : QSharedPointer<QObject>(ptr, d)
29 {
30 }
31
32 template <typename X>
QmlSharedPointerBase(const QSharedPointer<X> & other)33 QmlSharedPointerBase(const QSharedPointer<X> &other)
34 : QSharedPointer<QObject>(other)
35 {
36 }
37
QmlSharedPointerBase(const QmlSharedPointerBase & other)38 QmlSharedPointerBase(const QmlSharedPointerBase &other)
39 : QSharedPointer<QObject>(other)
40 {
41 }
42
43 template <typename X>
QmlSharedPointerBase(const QWeakPointer<X> & other)44 explicit QmlSharedPointerBase(const QWeakPointer<X> &other)
45 : QSharedPointer<QObject>(other)
46 {
47 }
48
49 QmlSharedPointerBase& operator=(const QmlSharedPointerBase& d)
50 {
51 QSharedPointer<QObject>::operator=(d);
52 return *this;
53 }
54
dynamic_metacall(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)55 static int dynamic_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
56 {
57 return _o->QObject::qt_metacall(_c, _id, _a);
58 }
59 };
60
61 template <class T, class B = QmlSharedPointerBase>
62 class QmlSharedPointer
63 : public B
64 {
65 // Q_OBJECT macro written out
66 public:
67 QT_WARNING_PUSH
68 Q_OBJECT_NO_OVERRIDE_WARNING
69 static const QMetaObject staticMetaObject;
70 QT_TR_FUNCTIONS
71 private:
72 Q_OBJECT_NO_ATTRIBUTES_WARNING
73 Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
74 QT_WARNING_POP
75 struct QPrivateSignal {};
76 QT_ANNOTATE_CLASS(qt_qobject, "")
77 // end Q_OBJECT
78
79 private:
init()80 void init()
81 {
82 Q_ASSERT(!QSharedPointer<QObject>::isNull());
83 // Connect all signals from the encapsulated item to the QmlSharedPointer
84 // Skip QObject's signals (e.g. destroyed)
85 const int qObjectMethodCount = QObject::staticMetaObject.methodCount();
86 for (int i = qObjectMethodCount; i < T::staticMetaObject.methodCount(); i++)
87 {
88 auto incomingSignal = T::staticMetaObject.method(i);
89 if (incomingSignal.methodType() != QMetaMethod::Signal) continue;
90 //qDebug() << "connect" << data() << i << incomingSignal.methodSignature();
91 QMetaObject::connect(data(), i, this, i);
92 }
93 }
deinit()94 void deinit()
95 {
96 // Disconnect all signals from the encapsulated item to the QmlSharedPointer
97 // Skip QObject's signals (e.g. destroyed)
98 const int qObjectMethodCount = QObject::staticMetaObject.methodCount();
99 for (int i = qObjectMethodCount; i < T::staticMetaObject.methodCount(); i++)
100 {
101 auto incomingSignal = T::staticMetaObject.method(i);
102 if (incomingSignal.methodType() != QMetaMethod::Signal) continue;
103 QMetaObject::disconnect(data(), i, this, i);
104 }
105 }
106 static const QByteArrayData *gen_stringdata();
107 static void findAndActivateSignal(QObject *_o, int _id, void **_a);
108
109 public:
dynamic_metacall(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)110 static int dynamic_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
111 {
112 //qDebug() << "Welcome to dynamic_metacall on" << staticMetaObject.className() << "with ID" << _id;
113 const int n_methods = staticMetaObject.methodCount() - staticMetaObject.superClass()->methodCount();
114 const int n_properties = staticMetaObject.propertyCount() - staticMetaObject.superClass()->propertyCount();
115 //qDebug() << "First off, lets call" << B::staticMetaObject.className() << "dynamic_metacall.";
116 _id = B::dynamic_metacall(_o, _c, _id, _a);
117 //qDebug() << "ID is now" << _id;
118 if (_id < 0)
119 return _id;
120 if (_c == QMetaObject::InvokeMetaMethod) {
121 if (_id < n_methods)
122 qt_static_metacall(_o, _c, _id, _a);
123 _id -= n_methods;
124 } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
125 if (_id < n_methods)
126 *reinterpret_cast<int*>(_a[0]) = -1;
127 _id -= n_methods;
128 }
129 else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
130 || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {
131 if (_id < n_properties)
132 qt_static_metacall(_o, _c, _id, _a);
133 _id -= n_properties;
134 } else if (_c == QMetaObject::QueryPropertyDesignable) {
135 _id -= n_properties;
136 } else if (_c == QMetaObject::QueryPropertyScriptable) {
137 _id -= n_properties;
138 } else if (_c == QMetaObject::QueryPropertyStored) {
139 _id -= n_properties;
140 } else if (_c == QMetaObject::QueryPropertyEditable) {
141 _id -= n_properties;
142 } else if (_c == QMetaObject::QueryPropertyUser) {
143 _id -= n_properties;
144 }
145 return _id;
146 }
147
QmlSharedPointer()148 QmlSharedPointer()
149 : B(new T())
150 {
151 init();
152 }
153
QmlSharedPointer(X * ptr)154 template <typename X> explicit QmlSharedPointer(X *ptr)
155 : B(ptr)
156 {
157 init();
158 }
159
QmlSharedPointer(X * ptr,Deleter d)160 template <typename X, typename Deleter> QmlSharedPointer(X *ptr, Deleter d)
161 : B(ptr, d)
162 {
163 init();
164 }
165
166 template <typename X>
QmlSharedPointer(const QSharedPointer<X> & other)167 QmlSharedPointer(const QSharedPointer<X> &other)
168 : B(other)
169 {
170 init();
171 }
172
QmlSharedPointer(const QmlSharedPointer<T,B> & other)173 QmlSharedPointer(const QmlSharedPointer<T, B> &other)
174 : B(other)
175 {
176 init();
177 }
178
179 template <typename X>
QmlSharedPointer(const QWeakPointer<X> & other)180 QmlSharedPointer(const QWeakPointer<X> &other)
181 : B(other)
182 {
183 init();
184 }
185
186 QmlSharedPointer<T, B>& operator=(const QmlSharedPointer<T, B>& d)
187 {
188 deinit();
189 B::operator=(d);
190 init();
191 return *this;
192 }
193
data()194 T *data() const
195 {
196 return (T *)QSharedPointer<QObject>::data();
197 }
198
199 T & operator*() const
200 {
201 return *data();
202 }
203
204 T * operator->() const
205 {
206 return data();
207 }
208
clone()209 virtual QmlSharedPointer<T, B> *clone()
210 {
211 return new QmlSharedPointer<T, B>(*this);
212 }
213
214 // The MOC doesn't generate this for us anymore
metaObject()215 const QMetaObject *metaObject() const
216 {
217 return &staticMetaObject;
218 }
219
qt_metacast(const char * _clname)220 void *qt_metacast(const char *_clname)
221 {
222 //qDebug() << "Attempted metacast" << _clname;
223 if (!_clname) return nullptr;
224
225 const QByteArrayDataPtr first = { const_cast<QByteArrayData*>(&staticMetaObject.d.stringdata[0]) };
226 //qDebug() << "new_stringdata[0]" << (QByteArray)first;
227
228 if (!strcmp(_clname, ((QByteArray)first).constData()))
229 return static_cast<void*>(this);
230 return B::qt_metacast(_clname);
231 }
232
qt_metacall(QMetaObject::Call _c,int _id,void ** _a)233 int qt_metacall(QMetaObject::Call _c, int _id, void **_a)
234 {
235 //Debug() << "qt_metacall" << _c << _id;
236 return dynamic_metacall(this, _c, _id, _a);
237 }
238 };
239
findAndActivateSignal(QObject * _o,int _id,void ** _a)240 template <typename T, typename B> void QmlSharedPointer<T, B>::findAndActivateSignal(QObject *_o, int _id, void **_a)
241 {
242 // Find which base class the given signal is for
243 auto current_metaobject = &staticMetaObject;
244 for (;;) {
245 auto super_metaobject = current_metaobject->superClass();
246 auto first_child_method = super_metaobject->methodOffset() + super_metaobject->methodCount();
247 if (_id >= first_child_method) {
248 QMetaObject::activate(_o, current_metaobject, _id - first_child_method, _a);
249 break;
250 }
251 current_metaobject = super_metaobject;
252 }
253 }
254
qt_static_metacall(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)255 template <typename T, typename B> void QmlSharedPointer<T, B>::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
256 {
257 auto *_t = static_cast<QmlSharedPointer<T, B> *>(_o);
258 auto *_child = _t->data();
259 Q_ASSERT(_child != nullptr);
260
261 //qDebug() << "Static metacall on..." << staticMetaObject.className() << _o << _c << _id;
262
263 if (_c == QMetaObject::InvokeMetaMethod) {
264 auto newId = _id + staticMetaObject.superClass()->methodCount();
265
266 auto method = QmlSharedPointer<T, B>::staticMetaObject.method(newId);
267 auto sig = QMetaObject::normalizedSignature(method.methodSignature());
268 if (method.methodType() == QMetaMethod::Signal) {
269 //qDebug() << "Firing signal" << sig;
270 QMetaObject::activate(_o, &staticMetaObject, _id, _a);
271 } else {
272 _child->qt_metacall(_c, newId, _a);
273 }
274 } else if (_c == QMetaObject::IndexOfMethod ||
275 _c == QMetaObject::ReadProperty ||
276 _c == QMetaObject::WriteProperty ||
277 _c == QMetaObject::ResetProperty) {
278 auto newId = _id + staticMetaObject.superClass()->propertyCount();
279 //qDebug() << "Access property" << newId;
280 _child->qt_metacall(_c, newId, _a);
281 } else {
282 Q_ASSERT(false); // Unhandled request
283 }
284 }
285
286 template<typename T, typename B>
gen_stringdata()287 const QByteArrayData *QmlSharedPointer<T, B>::gen_stringdata()
288 {
289 // The MOC always places the strings right after the QByteArrayDatas,
290 // so we can back out the number of strings based on the first offset
291 int n_strings = T::staticMetaObject.d.stringdata[0].offset / sizeof (T::staticMetaObject.d.stringdata[0]);
292
293 // Copy the child's string data
294 // (copy the lookup table, but don't copy the actual strings.
295 // Just point the new lookup table at the old strings)
296 const int MAX_N_STRINGS = 128;
297 Q_ASSERT(n_strings <= MAX_N_STRINGS);
298 static QByteArrayData new_stringdata[MAX_N_STRINGS];
299
300 for (int i=0; i<MAX_N_STRINGS; i++) {
301 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
302 new_stringdata[i].ref.atomic.store(-1); // Don't attempt to free
303 #else
304 new_stringdata[i].ref.atomic.storeRelaxed(-1); // Don't attempt to free
305 #endif
306 }
307
308 for (int i=0; i<n_strings; i++) {
309 new_stringdata[i].size = T::staticMetaObject.d.stringdata[i].size;
310 new_stringdata[i].offset = T::staticMetaObject.d.stringdata[i].offset + ((qptrdiff)&(T::staticMetaObject.d.stringdata[i]) - (qptrdiff)&new_stringdata[i]);
311 }
312
313 // Override the first string, which is the name:
314 const int MAX_N_CHARS = 128;
315 static char new_name[MAX_N_CHARS]{};
316 strcpy(new_name, "QmlSharedPointer<");
317 strncat(new_name, T::staticMetaObject.className(), 127);
318 strncat(new_name, ">", 127);
319 new_stringdata[0].size = strlen(new_name);
320 new_stringdata[0].offset = ((qptrdiff)&new_name - (qptrdiff)&new_stringdata[0]);
321
322 //const QByteArrayDataPtr first = { const_cast<QByteArrayData*>(&new_stringdata[0]) };
323 //qDebug() << "new_stringdata[0]" << (QByteArray)first;
324
325 return new_stringdata;
326 }
327
328 template<typename T, typename B> const QMetaObject QmlSharedPointer<T, B>::staticMetaObject = { {
329 &B::staticMetaObject,
330 QmlSharedPointer<T, B>::gen_stringdata(),
331 T::staticMetaObject.d.data,
332 QmlSharedPointer<T, B>::qt_static_metacall,
333 T::staticMetaObject.d.relatedMetaObjects,
334 T::staticMetaObject.d.extradata,
335 } };
336