UNISA Chatter – Design patterns in C++ Part 8: Reflection using QT
See UNISA – Summary of 2010 Posts for a list of related UNISA posts. Continued from UNISA Chatter – Design patterns in C++ Part 6: Widgets … Validation and Regular Expressions using QT.
IMPORTANT POINT: It is important to emphasize that the intent of these posts are to share my learning's as I dig through the last three subjects of my part-time UNISA studies. The posts by no means promote concepts or technologies … they are pure information sharing for fellow students … although the highlight that we explore more technologies and concepts that we typically prefer :)
Today we explore QT’s implementation of the MetaObject Pattern, which provide information about properties and methods of a QObject.
Technology | Description |
QT MetaObject Compiler | Also known as moc, generates the code that supports the reflection feature. |
QMetaObject | Supports the following functions:
|
DestType* qobject_cast<DestType*>(*) | Using the typecast operator we can convert an expression from one type to another. The destination type (DestType) is derived from QObject, making the operator a downcast operator. See dynamic_cast in QT help for more information. |
Properties versus Getters/Setters | Getters and setters are faster and more efficient. QObject Properties, however, are more generic. |
QVariant | Union wrapper for all the basic types and allowed Q_PROPERTY types. The struct delivers a rich interface for conversions and validations. |
DataObject | An extension to the QOject, which delivers additional features:
|
Here is an extract from the assignment code I wrote to explore the above:
Textbook Header File (take note of lines 10 – 15) …
1: #ifndef _TEXTBOOK_H_
2: #define _TEXTBOOK_H_
3:
4: #include <QObject>
5: #include <QString>
6: #include <QMap>
7:
8: class Textbook : public QObject {
9: Q_OBJECT
10: //{{Q_PROPERTY Marcos specified
11: Q_PROPERTY(QString author READ getAuthor WRITE setAuthor);
12: Q_PROPERTY(QString title READ getTitle WRITE setTitle);
13: Q_PROPERTY(QString isbn READ getIsbn WRITE setIsbn);
14: Q_PROPERTY(uint year READ getYearPub WRITE setYearPub);
15: //}
16: public:
17: Textbook(QString title, QString author, QString isbn, uint year);
18: //end
19: QString getAuthor() const;
20: QString getTitle() const;
21: QString getIsbn() const;
22: uint getYearPub() const;
23: QString toString() const;
24: public slots:
25: void setTitle(const QString& newTitle);
26: void setIsbn(const QString &newIsbn);
27: void setYearPub(uint newYear);
28: void setAuthor(const QString& newAuthor);
29:
30: //start
31: private:
32: uint m_YearPub;
33: QString m_Title, m_Author, m_Isbn;
34: };
35: #endif
36: Test Program
37: #include "textbook.h"
38: #include <QDebug>
39: #include <QMetaObject>
40: #include <QMetaProperty>
41: #include <QStringList>
42:
43: QString objToString(const QObject* obj) {
44: QStringList result;
45: const QMetaObject* meta = obj->metaObject();
46: result += "\n";
47: result += QString("Properties: %1").arg(meta->propertyCount());
48: result += QString("Property Macro methods: %1").arg(meta->methodCount());
49: result += QString("class %1 : public %2 {").arg(meta->className())
50: .arg(meta->superClass()->className());
51: for (int i=0; i < meta->propertyCount(); ++i) {
52: const QMetaProperty qmp = meta->property(i);
53: QVariant value = obj->property(qmp.name());
54: result += QString(" %1 %2 = %3;")
55: .arg(qmp.type())
56: .arg(qmp.name())
57: .arg(value.toString());
58: }
59: result += "};";
60: return result.join("\n");
61: }
62:
63: //end
64: //start id=client
65: int main() {
66: Textbook* t1 = new Textbook("The C++ Programming Language","Stroustrup", "0201700735", 1997);
67: Textbook* t2 = new Textbook("XML in a Nutshell", "Harold","0596002920", 2002);
68: Textbook* t3 = new Textbook("UML Distilled", "Fowler", "0321193687", 2004);
69: Textbook* t4 = new Textbook("Design Patterns", "Gamma", "0201633612",1995);
70: { /* Inner block for demonstration purposes */
71: qDebug() << objToString(t1);
72: qDebug() << objToString(t2);
73: qDebug() << objToString(t3);
74: qDebug() << objToString(t4);
75: } /* End of block - local variables destroyed. */
76: return 0;
77: }
78: //end
This results in the following code being generated and added to the resultant program …
1: /****************************************************************************
2: ** Meta object code from reading C++ file 'textbook.h'
3: **
4: ** Created: Wed Jun 2 07:32:12 2010
5: ** by: The Qt Meta Object Compiler version 61 (Qt 4.5.2)
6: **
7: ** WARNING! All changes made in this file will be lost!
8: *****************************************************************************/
9:
10: #include "../textbook.h"
11: #if !defined(Q_MOC_OUTPUT_REVISION)
12: #error "The header file 'textbook.h' doesn't include <QObject>."
13: #elif Q_MOC_OUTPUT_REVISION != 61
14: #error "This file was generated using the moc from 4.5.2. It"
15: #error "cannot be used with the include files from this version of Qt."
16: #error "(The moc has changed too much.)"
17: #endif
18:
19: QT_BEGIN_MOC_NAMESPACE
20: static const uint qt_meta_data_Textbook[] = {
21:
22: // content:
23: 2, // revision
24: 0, // classname
25: 0, 0, // classinfo
26: 4, 12, // methods
27: 4, 32, // properties
28: 0, 0, // enums/sets
29: 0, 0, // constructors
30:
31: // slots: signature, parameters, type, tag, flags
32: 19, 10, 9, 9, 0x0a,
33: 45, 37, 9, 9, 0x0a,
34: 70, 62, 9, 9, 0x0a,
35: 97, 87, 9, 9, 0x0a,
36:
37: // properties: name, type, flags
38: 124, 116, 0x0a095103,
39: 131, 116, 0x0a095103,
40: 137, 116, 0x0a095103,
41: 147, 142, 0x03095003,
42:
43: 0 // eod
44: };
45:
46: static const char qt_meta_stringdata_Textbook[] = {
47: "Textbook\0\0newTitle\0setTitle(QString)\0"
48: "newIsbn\0setIsbn(QString)\0newYear\0"
49: "setYearPub(uint)\0newAuthor\0"
50: "setAuthor(QString)\0QString\0author\0"
51: "title\0isbn\0uint\0year\0"
52: };
53:
54: const QMetaObject Textbook::staticMetaObject = {
55: { &QObject::staticMetaObject, qt_meta_stringdata_Textbook,
56: qt_meta_data_Textbook, 0 }
57: };
58:
59: const QMetaObject *Textbook::metaObject() const
60: {
61: return &staticMetaObject;
62: }
63:
64: void *Textbook::qt_metacast(const char *_clname)
65: {
66: if (!_clname) return 0;
67: if (!strcmp(_clname, qt_meta_stringdata_Textbook))
68: return static_cast<void*>(const_cast< Textbook*>(this));
69: return QObject::qt_metacast(_clname);
70: }
71:
72: int Textbook::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
73: {
74: _id = QObject::qt_metacall(_c, _id, _a);
75: if (_id < 0)
76: return _id;
77: if (_c == QMetaObject::InvokeMetaMethod) {
78: switch (_id) {
79: case 0: setTitle((*reinterpret_cast< const QString(*)>(_a[1]))); break;
80: case 1: setIsbn((*reinterpret_cast< const QString(*)>(_a[1]))); break;
81: case 2: setYearPub((*reinterpret_cast< uint(*)>(_a[1]))); break;
82: case 3: setAuthor((*reinterpret_cast< const QString(*)>(_a[1]))); break;
83: default: ;
84: }
85: _id -= 4;
86: }
87: #ifndef QT_NO_PROPERTIES
88: else if (_c == QMetaObject::ReadProperty) {
89: void *_v = _a[0];
90: switch (_id) {
91: case 0: *reinterpret_cast< QString*>(_v) = getAuthor(); break;
92: case 1: *reinterpret_cast< QString*>(_v) = getTitle(); break;
93: case 2: *reinterpret_cast< QString*>(_v) = getIsbn(); break;
94: case 3: *reinterpret_cast< uint*>(_v) = getYearPub(); break;
95: }
96: _id -= 4;
97: } else if (_c == QMetaObject::WriteProperty) {
98: void *_v = _a[0];
99: switch (_id) {
100: case 0: setAuthor(*reinterpret_cast< QString*>(_v)); break;
101: case 1: setTitle(*reinterpret_cast< QString*>(_v)); break;
102: case 2: setIsbn(*reinterpret_cast< QString*>(_v)); break;
103: case 3: setYearPub(*reinterpret_cast< uint*>(_v)); break;
104: }
105: _id -= 4;
106: } else if (_c == QMetaObject::ResetProperty) {
107: _id -= 4;
108: } else if (_c == QMetaObject::QueryPropertyDesignable) {
109: _id -= 4;
110: } else if (_c == QMetaObject::QueryPropertyScriptable) {
111: _id -= 4;
112: } else if (_c == QMetaObject::QueryPropertyStored) {
113: _id -= 4;
114: } else if (_c == QMetaObject::QueryPropertyEditable) {
115: _id -= 4;
116: } else if (_c == QMetaObject::QueryPropertyUser) {
117: _id -= 4;
118: }
119: #endif // QT_NO_PROPERTIES
120: return _id;
121: }
122: QT_END_MOC_NAMESPACE
Which we can analyze with a function such as …
1: QString objToString(const QObject* obj) {
2: QStringList result;
3: const QMetaObject* meta = obj->metaObject();
4: result += "\n";
5: result += QString("Properties: %1").arg(meta->propertyCount());
6: result += QString("Property Macro methods: %1").arg(meta->methodCount());
7: result += QString("class %1 : public %2 {").arg(meta->className())
8: .arg(meta->superClass()->className());
9: for (int i=0; i < meta->propertyCount(); ++i) {
10: const QMetaProperty qmp = meta->property(i);
11: QVariant value = obj->property(qmp.name());
12: result += QString(" %1 %2 = %3;")
13: .arg(qmp.type())
14: .arg(qmp.name())
15: .arg(value.toString());
16: }
17: result += "};";
18: return result.join("\n");
19: }
Giving us this result …
1: "
2: Properties: 5
3: Property Macro methods: 8
4: class Textbook : public QObject {
5: 10 objectName = 0201700735;
6: 10 author = Stroustrup;
7: 10 title = The C++ Programming Language;
8: 10 isbn = 0201700735;
9: 3 year = 1997;
10: };"
Note that we have eight property macro methods, which are the four getters and the four setters we defined. We also have five properties … why five and not four? We have our four properties and the “name” property that is defined within QObject.
If you are working in Visual Studio 2010, as I am, you may want to look at the following information which is kind-of-related:
- Using Properties (C# Programming Guide) … https://msdn.microsoft.com/en-us/library/w86s7x04.aspx
- Attributes (C# and Visual Basic) … https://msdn.microsoft.com/en-us/library/z0w1kczw.aspx
- Accessing Attributes by Using Reflection (C# and Visual Basic) … https://msdn.microsoft.com/en-us/library/z919e8tw.aspx
- Reflection (C# and Visual Basic) … https://msdn.microsoft.com/en-us/library/ms173183.aspx
Yippee, next time we will look at more design patterns and summarize all of the patterns we have encountered during our UNISA adventure this year :)
Comments
- Anonymous
June 09, 2010
Thanks for the explanations!! You've explained a few concepts that I've struggled with Keep up the hard work .:)