Willy's Reflections

| Willy-Peter Schaub | Visual Studio ALM Rangers | In search of IT simplicity, quality and tranquility |

UNISA Chatter – Design patterns in C++ Part 8: Reflection using QT

UNISA Chatter – Design patterns in C++ Part 8: Reflection using QT

Rate This
  • Comments 1

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:

  • className()  returns the class name
  • superClass() returns a pointer to the QMetaObject of the base class or 0.
  • methodCount() returns the number of member functions of the class
  • … and lots more … refer to QT help :)
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:

  • virtual interface for obtaining properties
  • copying and comparing functions
  • toString() function that returns a presentation of all the properties of the object in XML format

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:

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 :)

  • Thanks for the explanations!!

    You've explained a few concepts that I've struggled with

    Keep up the hard work .:)

Page 1 of 1 (1 items)
Leave a Comment
  • Please add 3 and 2 and type the answer here:
  • Post