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: |
33 | class 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 | |
40 | public /* 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 | |
52 | public /* 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. |
65 | W_OBJECT_IMPL(MyObject) |
66 | |
67 | // That's it! MyObject is a QObject that can be used in QML or connected. |
68 | void 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 **/ |
80 | class 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: */ |
95 | protected: |
96 | // Declares a protected slot |
97 | void protectedSlot() {} |
98 | W_SLOT(protectedSlot) |
99 | private: |
100 | // and a private slot |
101 | void privateSlot() {} |
102 | W_SLOT(privateSlot) |
103 | |
104 | public: |
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)) |
112 | private: |
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 | |
120 | W_OBJECT_IMPL(SlotTutorial) |
121 | |
122 | |
123 | /** ******************************************************************************************** **/ |
124 | /** SIGNALS **/ |
125 | |
126 | class 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 | |
137 | public: |
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 | |
150 | W_OBJECT_IMPL(SignalTutorial) |
151 | |
152 | |
153 | /** ******************************************************************************************** **/ |
154 | /** Gadgets, invokable, constructor **/ |
155 | |
156 | |
157 | class InvokableTutorial { |
158 | // Just like Qt has Q_GADGET, here we have W_GADGET |
159 | W_GADGET(InvokableTutorial) |
160 | |
161 | public: |
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 |
183 | W_GADGET_IMPL(InvokableTutorial) |
184 | |
185 | /** ******************************************************************************************** **/ |
186 | /** PROPERTY **/ |
187 | |
188 | #include <QtCore/QMap> |
189 | |
190 | class PropertyTutorial : public QObject { |
191 | W_OBJECT(PropertyTutorial) |
192 | |
193 | public: |
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 | |
230 | W_OBJECT_IMPL(PropertyTutorial) |
231 | |
232 | |
233 | /** ******************************************************************************************** **/ |
234 | /** Enums **/ |
235 | |
236 | class EnumTutorial { |
237 | W_GADGET(EnumTutorial) |
238 | |
239 | public: |
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 | |
257 | W_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 | |
272 | struct CustomType1 {}; |
273 | struct CustomType2 {}; |
274 | struct 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 | */ |
281 | W_REGISTER_ARGTYPE(CustomType1) |
282 | W_REGISTER_ARGTYPE(CustomType1*) |
283 | W_REGISTER_ARGTYPE(CustomType2) |
284 | |
285 | class ArgTypes : public QObject { |
286 | W_OBJECT(ArgTypes) |
287 | public: |
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 | |
304 | W_OBJECT_IMPL(ArgTypes) |
305 | |
306 | /** ******************************************************************************************** **/ |
307 | /** TEMPLATES **/ |
308 | |
309 | #include <QtCore/QDebug> |
310 | |
311 | // We can have templated class: |
312 | template<typename T> |
313 | class MyTemplate : public QObject { |
314 | W_OBJECT(MyTemplate) |
315 | public: |
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: |
326 | W_OBJECT_IMPL(MyTemplate<T>, template <typename T>) |
327 | |
328 | // When you have several template arguments: |
329 | template<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: |
333 | W_OBJECT_IMPL((MyTemplate2<A,B>), template<typename A, typename B>) |
334 | |
335 | |
336 | void 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: |
350 | struct MyStruct { |
351 | class Nested : public QObject { |
352 | W_OBJECT(Nested) |
353 | public: |
354 | int foobar() const { return 0; } |
355 | W_INVOKABLE(foobar) |
356 | }; |
357 | }; |
358 | W_OBJECT_IMPL(MyStruct::Nested) |
359 | |
360 | /** ******************************************************************************************** **/ |
361 | |
362 | int main() { |
363 | MyObject o; |
364 | aaa(&o); |
365 | templ(); |
366 | } |
367 | |