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