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 flags:
88 - Specifying the the access: W_Access::Protected, W_Access::Private
89 or W_Access::Public (the default)
90 - W_Compat: for deprecated methods (equivalent of Q_MOC_COMPAT
91
92 The W_SLOT macro can optionally have a list of parameter types as second
93 argument to disambiguate or declare types.
94 */
95
96 /* Examples: */
97protected:
98 // Declares a protected slot
99 void protectedSlot() {}
100 W_SLOT(protectedSlot, W_Access::Protected)
101private:
102 // and a private slot
103 void privateSlot() {}
104 W_SLOT(privateSlot, W_Access::Private)
105
106public:
107 // Overloaded function needs a parameter list as second argument of the macro
108 // to disambiguate
109 void overload() {}
110 W_SLOT(overload, ())
111
112 void overload(int) {}
113 W_SLOT(overload, (int))
114private:
115 void overload(double) {}
116 W_SLOT(overload, (double), W_Access::Private)
117 void overload(int, int) {}
118 W_SLOT(overload, (int, int), W_Access::Private)
119 // Note: for custom type that are not const reference, one must use the normalized signature
120};
121
122W_OBJECT_IMPL(SlotTutorial)
123
124
125/** ******************************************************************************************** **/
126/** SIGNALS **/
127
128class SignalTutorial : public QObject {
129 W_OBJECT(SignalTutorial)
130
131 /**
132 <signal signature>
133 W_SIGNAL( <signal name> [, (<parameter types>) ] , <parameter names> )
134
135 Unlike W_SLOT, W_SIGNAL must be placed directly after the signal signature declaration.
136 There should not be a semi colon after the signal signature.
137 */
138
139public:
140 // Example:
141 void sig1(int a , int b)
142 W_SIGNAL(sig1, a, b)
143
144 // Or on the same line
145 void sig2(int a, int b) W_SIGNAL(sig2, a, b)
146
147 // For overloaded signals:
148 void overload(int a, int b)
149 W_SIGNAL(overload, (int, int), a, b)
150};
151
152W_OBJECT_IMPL(SignalTutorial)
153
154
155/** ******************************************************************************************** **/
156/** Gadgets, invokable, constructor **/
157
158
159class InvokableTutorial {
160 // Just like Qt has Q_GADGET, here we have W_GADGET
161 W_GADGET(InvokableTutorial)
162
163public:
164 /** W_INVOKABLE is the same as W_SLOT.
165 * It can take another flag (W_Scriptable) which corresponds to Q_SCRIPTABLE */
166 void myInvokable() {}
167 W_INVOKABLE(myInvokable)
168
169
170 /** W_CONSTRUCTOR(<parameter types>)
171 for Q_INVOKABLE constructor, just pass the parameter types to this macro.
172 one can have W_CONSTRUCTOR() for the default constructor even if it is implicit
173 */
174
175 InvokableTutorial(int, int) {}
176 W_CONSTRUCTOR(int, int)
177
178 InvokableTutorial(void*, void* =nullptr) {}
179 W_CONSTRUCTOR(void*, void*)
180 // Because of the default argument we can also do that: (only in this macro)
181 W_CONSTRUCTOR(void*)
182};
183
184// For gadget there is also a different IMPL macro
185W_GADGET_IMPL(InvokableTutorial)
186
187/** ******************************************************************************************** **/
188/** PROPERTY **/
189
190#include <QtCore/QMap>
191
192class PropertyTutorial : public QObject {
193 W_OBJECT(PropertyTutorial)
194
195public:
196 /** W_PROPERTY(<type>, <name> [, <flags>]*)
197
198 There are the macro READ WRITE MEMBER and so on which have been defined so
199 you can just add a comma after the type, just like in a Q_PROPERTY.
200
201 W_PROPERTY need to be put after all the setters, getters, signals and members
202 have been declared.
203 */
204
205 QString m_value;
206 QString value() const { return m_value; }
207 void setValue(const QString &value) {
208 m_value = value;
209 emit valueChanged();
210 }
211 void valueChanged()
212 W_SIGNAL(valueChanged)
213
214 // Just like in Qt only with one additional comma after the type
215 W_PROPERTY(QString, prop1 READ value WRITE setValue NOTIFY valueChanged)
216
217 // Is equivalent to:
218 W_PROPERTY(QString, prop2, &PropertyTutorial::value, &PropertyTutorial::setValue,
219 W_Notify, &PropertyTutorial::valueChanged)
220 // The setter and getter are matched by signature. add W_Notify before the notify signal
221
222 // By member:
223 W_PROPERTY(QString, prop3 MEMBER m_value NOTIFY valueChanged)
224 //equivalent to
225 W_PROPERTY(QString, prop4, &PropertyTutorial::m_value, W_Notify, &PropertyTutorial::valueChanged)
226
227 // Optionally, you can put parentheses around the type, useful if it contains a comma
228 QMap<int, int> m_map;
229 W_PROPERTY((QMap<int,int>), map MEMBER m_map)
230};
231
232W_OBJECT_IMPL(PropertyTutorial)
233
234
235/** ******************************************************************************************** **/
236/** Enums **/
237
238class EnumTutorial {
239 W_GADGET(EnumTutorial)
240
241public:
242 /** W_ENUM(<name>, <values>)
243 Similar to Q_ENUM, but we also have to manually write all the values.
244 Maybe in the future it could be made nicer that declares the enum in the same macro
245 but now that's all we have */
246 enum MyEnum { Blue, Red, Green, Yellow = 45, Violet = Blue + Green*3 };
247
248 W_ENUM(MyEnum, Blue, Red, Green, Yellow)
249
250 // CS_ENUM is a bit better, but i don't believe CopperSpice works with complex expressions
251 // such as "Blue + Green*3".
252
253 // W_ENUM is currently limited to 16 enum values.
254 // enum class are not yet supported
255
256 // There is a W_FLAG which is the same as Q_FLAG
257};
258
259W_GADGET_IMPL(EnumTutorial)
260
261/** ******************************************************************************************** **/
262/** TYPE REGISTRATION **/
263
264/* Here is where the thing gets a bit more awkward:
265 The types of parameters of signals and slots need to be registered so that we can generate the
266 function signature
267
268 To use a type as a return type or signal slot parameter, it needs to:
269 - be a builtin QMetaType; or
270 - be registered with W_REGISTER_ARGTYPE; or
271 - use the overload syntax, but not with const reference.
272*/
273
274struct CustomType1 {};
275struct CustomType2 {};
276struct CustomType3 {};
277
278/** W_REGISTER_ARGTYPE(TYPE)
279 register TYPE so it can be used as a parameter of a signal/slot or return value
280 One must use the normalized signature.
281 Note: This does not imply Q_DECLARE_METATYPE, and Q_DECLARE_METATYPE does not emply this.
282 */
283W_REGISTER_ARGTYPE(CustomType1)
284W_REGISTER_ARGTYPE(CustomType1*)
285W_REGISTER_ARGTYPE(CustomType2)
286
287class FooBar : public QObject {
288 W_OBJECT(FooBar)
289public:
290 void slot1(CustomType1 a, CustomType2 b) {}
291 W_SLOT(slot1) // OK, all arguments register with W_REGISTER_ARGTYPE
292
293 void slot2(CustomType1 *a, CustomType2 *b) {}
294 W_SLOT(slot2, (CustomType1*,CustomType2*)) // Need to use the overload syntax because
295 // CustomType2* is not registered
296
297 typedef int MyInt;
298 typedef CustomType1 MyCustomType1;
299
300 void slot3(FooBar::MyInt a, FooBar::MyCustomType1 b) {}
301 W_SLOT(slot3, (FooBar::MyInt,FooBar::MyCustomType1)) // Need to use the overload syntax to use
302 // different type name (typedefs)
303
304};
305
306W_OBJECT_IMPL(FooBar)
307
308/** ******************************************************************************************** **/
309/** TEMPLATES **/
310
311#include <QtCore/QDebug>
312
313// We can have templated class:
314template<typename T>
315class MyTemplate : public QObject {
316 W_OBJECT(MyTemplate)
317public:
318 // Template class can have slots and signals that depends on the parameter:
319 void slot(T t) { qDebug() << "templated slot" << t; }
320 W_SLOT(slot)
321
322 void signal(T t)
323 W_SIGNAL(signal, t)
324};
325
326//The syntax of W_OBJECT_IMPL changes a bit: as a second parameter you need to specify the template
327//prefix:
328W_OBJECT_IMPL(MyTemplate<T>, template <typename T>)
329
330// When you have several template arguments:
331template<typename A, typename B> class MyTemplate2 : public QObject {
332 W_OBJECT(MyTemplate2)
333};
334// The first argument of W_OBJECT_IMPL need to be within parentheses:
335W_OBJECT_IMPL((MyTemplate2<A,B>), template<typename A, typename B>)
336
337
338void templ() {
339 // This shows that it is possible;
340 bool ok = true;
341 MyTemplate<QString> obj;
342 // old syntax
343 ok = ok && QObject::connect(&obj, SIGNAL(signal(QString)), &obj, SLOT(slot(QString)));
344 // new syntax
345 ok = ok && QObject::connect(&obj, &MyTemplate<QString>::signal, &obj, &MyTemplate<QString>::slot);
346 Q_ASSERT(ok);
347 emit obj.signal("Hallo"); // Will show the qDebug twice
348}
349
350/** ******************************************************************************************** **/
351// Nested classes are possible:
352struct MyStruct {
353 class Nested : public QObject {
354 W_OBJECT(Nested)
355 public:
356 int foobar() const { return 0; }
357 W_INVOKABLE(foobar)
358 };
359};
360W_OBJECT_IMPL(MyStruct::Nested)
361
362/** ******************************************************************************************** **/
363
364int main() {
365 MyObject o;
366 aaa(&o);
367 templ();
368}
369