1/**
2 This file presents Verdigris, a "fork" of CopperSpice.
3
4 CopperSpice is a fork of Qt 4 whose specificity is to get rid of moc (Qt Meta-object compiler)
5 In order to get rid of moc, they changed the Qt macros to be less optimal.
6 They made a complete, incompatible fork of Qt.
7
8 Verdigris is not a fork of CopperSpice, but rather a re-implementation of their macro in a way
9 that is binary compatible with Qt. You can write your application without needing moc, and still
10 use it with existing Qt 5 releases.
11
12 CopperSpice generates the metaobjects at run-time. But moc generates them at compile time.
13 Verdigris uses constexpr to generate QMetaObject at compile time.
14 It is using C++14 for simplicity because it is much more easy to handle constexpr.
15 The code is originally taken from my previous work
16 https://woboq.com/blog/reflection-in-cpp-and-qt-moc.html
17 In that experiment, I was trying to use same macros that Qt does (keeping source compatibility).
18 When using more complex but uglier macros (CopperSpice style) we can do it without the moc.
19
20 */
21
22
23/** ******************************************************************************************** **/
24/** INTRODUCTION **/
25
26// In a header file you would include the wobjectdefs.h header
27// This is equivalent to the qobjectdefs.h header
28#include <wobjectdefs.h>
29// And because you will inherit from QObject you also need to QObject header
30#include <QObject>
31
32// Now declare your class:
33class MyObject : public QObject
34{
35 /** The W_OBJECT macro is equivalent to the Q_OBJECT macro. The difference is that it must
36 contains the class name as a parameter and need to be put before any other W_ macro in the
37 class. So it's the same as the CS_OBJECT macro from CopperSpice. */
38 W_OBJECT(MyObject)
39
40public /* slots */:
41
42 // Here we declare a slot:
43 void mySlot(const QString &name) { qDebug("hello %s", qPrintable(name)); }
44 /* If you're going to use the new connection syntax, no need to do anything else for slots.
45 But if you want to use the other connection syntax, or QML, you need to register the slot
46 just like so: */
47 W_SLOT(mySlot)
48 /* The W_SLOT has optional arguments that we will see later. It is already much simpler than
49 the two CopperSpice macros: CS_SLOT_1 and CS_SLOT_2. Also, CopperSpice slots cannot be
50 declared inline in the class definition. */
51
52public /* signals */:
53
54 // Now a signal:
55 void mySignal(const QString &name)
56 W_SIGNAL(mySignal, name)
57 /* Note the absence of semi colon after the signal declaration */
58};
59
60/* Here is what would go in the C++ .cpp file: */
61#include <wobjectimpl.h>
62
63// And now this is the macro you need to instantiate the meta object.
64// It's an additional macro that basically does the same as the code generated by moc.
65W_OBJECT_IMPL(MyObject)
66
67// That's it! MyObject is a QObject that can be used in QML or connected.
68void aaa(MyObject *obj1) {
69 bool ok = true;
70 // new syntax
71 ok = ok && QObject::connect(obj1, &MyObject::mySignal, obj1, &MyObject::mySlot);
72 // old syntax
73 ok = ok && QObject::connect(obj1, SIGNAL(mySignal(QString)), obj1, SLOT(mySlot(QString)));
74 Q_ASSERT(ok);
75}
76
77
78/** ******************************************************************************************** **/
79/** SLOTS **/
80class SlotTutorial : public QObject {
81 W_OBJECT(SlotTutorial)
82
83 /**
84 W_SLOT( <slot name> [, (<parameters types>) ] [, <flags>]* )
85
86 The W_SLOT macro needs to be put after the slot declaration.
87 The W_SLOT macro can have the W_Compat for deprecated methods
88 (equivalent of Q_MOC_COMPAT
89
90 The W_SLOT macro can optionally have a list of parameter types as second
91 argument to disambiguate or declare types.
92 */
93
94 /* Examples: */
95protected:
96 // Declares a protected slot
97 void protectedSlot() {}
98 W_SLOT(protectedSlot)
99private:
100 // and a private slot
101 void privateSlot() {}
102 W_SLOT(privateSlot)
103
104public:
105 // Overloaded function needs a parameter list as second argument of the macro
106 // to disambiguate
107 void overload() {}
108 W_SLOT(overload, ())
109
110 void overload(int) {}
111 W_SLOT(overload, (int))
112private:
113 void overload(double) {}
114 W_SLOT(overload, (double))
115 void overload(int, int) {}
116 W_SLOT(overload, (int, int))
117 // Note: for custom type that are not const reference, one must use the normalized signature
118};
119
120W_OBJECT_IMPL(SlotTutorial)
121
122
123/** ******************************************************************************************** **/
124/** SIGNALS **/
125
126class SignalTutorial : public QObject {
127 W_OBJECT(SignalTutorial)
128
129 /**
130 <signal signature>
131 W_SIGNAL( <signal name> [, (<parameter types>) ] , <parameter names> )
132
133 Unlike W_SLOT, W_SIGNAL must be placed directly after the signal signature declaration.
134 There should not be a semi colon after the signal signature.
135 */
136
137public:
138 // Example:
139 void sig1(int a , int b)
140 W_SIGNAL(sig1, a, b)
141
142 // Or on the same line
143 void sig2(int a, int b) W_SIGNAL(sig2, a, b)
144
145 // For overloaded signals:
146 void overload(int a, int b)
147 W_SIGNAL(overload, (int, int), a, b)
148};
149
150W_OBJECT_IMPL(SignalTutorial)
151
152
153/** ******************************************************************************************** **/
154/** Gadgets, invokable, constructor **/
155
156
157class InvokableTutorial {
158 // Just like Qt has Q_GADGET, here we have W_GADGET
159 W_GADGET(InvokableTutorial)
160
161public:
162 /** W_INVOKABLE is the same as W_SLOT.
163 * It can take another flag (W_Scriptable) which corresponds to Q_SCRIPTABLE */
164 void myInvokable() {}
165 W_INVOKABLE(myInvokable)
166
167
168 /** W_CONSTRUCTOR(<parameter types>)
169 for Q_INVOKABLE constructor, just pass the parameter types to this macro.
170 one can have W_CONSTRUCTOR() for the default constructor even if it is implicit
171 */
172
173 InvokableTutorial(int, int) {}
174 W_CONSTRUCTOR(int, int)
175
176 InvokableTutorial(void*, void* =nullptr) {}
177 W_CONSTRUCTOR(void*, void*)
178 // Because of the default argument we can also do that: (only in this macro)
179 W_CONSTRUCTOR(void*)
180};
181
182// For gadget there is also a different IMPL macro
183W_GADGET_IMPL(InvokableTutorial)
184
185/** ******************************************************************************************** **/
186/** PROPERTY **/
187
188#include <QtCore/QMap>
189
190class PropertyTutorial : public QObject {
191 W_OBJECT(PropertyTutorial)
192
193public:
194 /** W_PROPERTY(<type>, <name> [, <flags>]*)
195
196 There are the macro READ WRITE MEMBER and so on which have been defined so
197 you can just add a comma after the type, just like in a Q_PROPERTY.
198
199 W_PROPERTY need to be put after all the setters, getters, signals and members
200 have been declared.
201 */
202
203 QString m_value;
204 QString value() const { return m_value; }
205 void setValue(const QString &value) {
206 m_value = value;
207 emit valueChanged();
208 }
209 void valueChanged()
210 W_SIGNAL(valueChanged)
211
212 // Just like in Qt only with one additional comma after the type
213 W_PROPERTY(QString, prop1 READ value WRITE setValue NOTIFY valueChanged)
214
215 // Is equivalent to:
216 W_PROPERTY(QString, prop2, &PropertyTutorial::value, &PropertyTutorial::setValue,
217 W_Notify, &PropertyTutorial::valueChanged)
218 // The setter and getter are matched by signature. add W_Notify before the notify signal
219
220 // By member:
221 W_PROPERTY(QString, prop3 MEMBER m_value NOTIFY valueChanged)
222 //equivalent to
223 W_PROPERTY(QString, prop4, &PropertyTutorial::m_value, W_Notify, &PropertyTutorial::valueChanged)
224
225 // Optionally, you can put parentheses around the type, useful if it contains a comma
226 QMap<int, int> m_map;
227 W_PROPERTY((QMap<int,int>), map MEMBER m_map)
228};
229
230W_OBJECT_IMPL(PropertyTutorial)
231
232
233/** ******************************************************************************************** **/
234/** Enums **/
235
236class EnumTutorial {
237 W_GADGET(EnumTutorial)
238
239public:
240 /** W_ENUM(<name>, <values>)
241 Similar to Q_ENUM, but we also have to manually write all the values.
242 Maybe in the future it could be made nicer that declares the enum in the same macro
243 but now that's all we have */
244 enum MyEnum { Blue, Red, Green, Yellow = 45, Violet = Blue + Green*3 };
245
246 W_ENUM(MyEnum, Blue, Red, Green, Yellow)
247
248 // CS_ENUM is a bit better, but i don't believe CopperSpice works with complex expressions
249 // such as "Blue + Green*3".
250
251 // W_ENUM is currently limited to 16 enum values.
252 // enum class are not yet supported
253
254 // There is a W_FLAG which is the same as Q_FLAG
255};
256
257W_GADGET_IMPL(EnumTutorial)
258
259/** ******************************************************************************************** **/
260/** TYPE REGISTRATION **/
261
262/* Here is where the thing gets a bit more awkward:
263 The types of parameters of signals and slots need to be registered so that we can generate the
264 function signature
265
266 To use a type as a return type or signal slot parameter, it needs to:
267 - be a builtin QMetaType; or
268 - be registered with W_REGISTER_ARGTYPE; or
269 - use the overload syntax, but not with const reference.
270*/
271
272struct CustomType1 {};
273struct CustomType2 {};
274struct CustomType3 {};
275
276/** W_REGISTER_ARGTYPE(TYPE)
277 register TYPE so it can be used as a parameter of a signal/slot or return value
278 One must use the normalized signature.
279 Note: This does not imply Q_DECLARE_METATYPE, and Q_DECLARE_METATYPE does not imply this.
280 */
281W_REGISTER_ARGTYPE(CustomType1)
282W_REGISTER_ARGTYPE(CustomType1*)
283W_REGISTER_ARGTYPE(CustomType2)
284
285class ArgTypes : public QObject {
286 W_OBJECT(ArgTypes)
287public:
288 void slot1(CustomType1, CustomType2) {}
289 W_SLOT(slot1) // OK, all arguments register with W_REGISTER_ARGTYPE
290
291 void slot2(CustomType1 *, CustomType2 *) {}
292 W_SLOT(slot2, (CustomType1*,CustomType2*)) // Need to use the overload syntax because
293 // CustomType2* is not registered
294
295 typedef int MyInt;
296 typedef CustomType1 MyCustomType1;
297
298 void slot3(ArgTypes::MyInt, ArgTypes::MyCustomType1) {}
299 W_SLOT(slot3, (ArgTypes::MyInt,ArgTypes::MyCustomType1)) // Need to use the overload syntax to use
300 // different type name (typedefs)
301
302};
303
304W_OBJECT_IMPL(ArgTypes)
305
306/** ******************************************************************************************** **/
307/** TEMPLATES **/
308
309#include <QtCore/QDebug>
310
311// We can have templated class:
312template<typename T>
313class MyTemplate : public QObject {
314 W_OBJECT(MyTemplate)
315public:
316 // Template class can have slots and signals that depends on the parameter:
317 void slot(T t) { qDebug() << "templated slot" << t; }
318 W_SLOT(slot)
319
320 void signal(T t)
321 W_SIGNAL(signal, t)
322};
323
324//The syntax of W_OBJECT_IMPL changes a bit: as a second parameter you need to specify the template
325//prefix:
326W_OBJECT_IMPL(MyTemplate<T>, template <typename T>)
327
328// When you have several template arguments:
329template<typename A, typename B> class MyTemplate2 : public QObject {
330 W_OBJECT(MyTemplate2)
331};
332// The first argument of W_OBJECT_IMPL need to be within parentheses:
333W_OBJECT_IMPL((MyTemplate2<A,B>), template<typename A, typename B>)
334
335
336void templ() {
337 // This shows that it is possible;
338 bool ok = true;
339 MyTemplate<QString> obj;
340 // old syntax
341 ok = ok && QObject::connect(&obj, SIGNAL(signal(QString)), &obj, SLOT(slot(QString)));
342 // new syntax
343 ok = ok && QObject::connect(&obj, &MyTemplate<QString>::signal, &obj, &MyTemplate<QString>::slot);
344 Q_ASSERT(ok);
345 emit obj.signal("Hallo"); // Will show the qDebug twice
346}
347
348/** ******************************************************************************************** **/
349// Nested classes are possible:
350struct MyStruct {
351 class Nested : public QObject {
352 W_OBJECT(Nested)
353 public:
354 int foobar() const { return 0; }
355 W_INVOKABLE(foobar)
356 };
357};
358W_OBJECT_IMPL(MyStruct::Nested)
359
360/** ******************************************************************************************** **/
361
362int main() {
363 MyObject o;
364 aaa(&o);
365 templ();
366}
367