qttemplatespropertiesmoc

Qt Q_PROPERTY with template accessors


I'm aiming for a little bit more code reuse, while maintaining verbosity.

Consider the following sample code:

// qdot@defixio /tmp/test4 $ cat test.h
#include <QObject>

class Foo : public QObject {
  Q_OBJECT
  // Q_PROPERTY(int bar1 READ bar<1>)
  // Q_PROPERTY(int bar2 READ bar<2>)

  public:
  template <int i> int bar() const;
};

// qdot@defixio /tmp/test4 $ cat test.cpp 
#include "test.h"
#include <QDebug>


template <int i> 
int Foo::bar() const { qDebug() << "Template parameter" <<  i; } 

int main() {
  Foo foo;
  foo.bar<1>();
  foo.bar<2>();
  return 0;
}

This one compiles and runs as expected.

In case you're wondering why I would like that - imagine a set of properties, DESIGNABLE, etc, but falling under the same "class" - in that case, I would like to have separate properties, using enum-templatetyped accessors.

Uncommenting the property definitions, results in the following moc error:

/usr/bin/moc -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. test.h -o moc_test.cpp
test.h:5: Parse error at "bar"

Any idea on how to properly mix templating and moc?


To answer cmannet85's comment, and add more insight - yes, that moc invocation generates moc_test.cpp from moc.h.

To test and demonstrate it further, I added another property

Q_PROPERTY(int baz1 READ baz1)

and the diff between moc_test.cpp before and after is this:

--- moc_test.cpp        2012-10-02 13:23:39.442333849 +0200
+++ moc_test_baz1.cpp   2012-10-02 13:23:29.822328462 +0200
@@ -1,7 +1,7 @@
 /****************************************************************************
 ** Meta object code from reading C++ file 'test.h'
 **
-** Created: Tue Oct 2 13:23:39 2012
+** Created: Tue Oct 2 13:22:27 2012
 **      by: The Qt Meta Object Compiler version 63 (Qt 4.8.3)
 **
 ** WARNING! All changes made in this file will be lost!
@@ -24,17 +24,20 @@
        0,       // classname
        0,    0, // classinfo
        0,    0, // methods
-       0,    0, // properties
+       1,   14, // properties
        0,    0, // enums/sets
        0,    0, // constructors
        0,       // flags
        0,       // signalCount

+ // properties: name, type, flags
+       8,    4, 0x02095001,
+
        0        // eod
 };

 static const char qt_meta_stringdata_Foo[] = {
-    "Foo\0"
+    "Foo\0int\0baz1\0"
 };

 void Foo::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
@@ -76,6 +79,30 @@
     _id = QObject::qt_metacall(_c, _id, _a);
     if (_id < 0)
         return _id;
+    
+#ifndef QT_NO_PROPERTIES
+     if (_c == QMetaObject::ReadProperty) {
+        void *_v = _a[0];
+        switch (_id) {
+        case 0: *reinterpret_cast< int*>(_v) = baz1(); break;
+        }
+        _id -= 1;
+    } else if (_c == QMetaObject::WriteProperty) {
+        _id -= 1;
+    } else if (_c == QMetaObject::ResetProperty) {
+        _id -= 1;
+    } else if (_c == QMetaObject::QueryPropertyDesignable) {
+        _id -= 1;
+    } else if (_c == QMetaObject::QueryPropertyScriptable) {
+        _id -= 1;
+    } else if (_c == QMetaObject::QueryPropertyStored) {
+        _id -= 1;
+    } else if (_c == QMetaObject::QueryPropertyEditable) {
+        _id -= 1;
+    } else if (_c == QMetaObject::QueryPropertyUser) {
+        _id -= 1;
+    }
+#endif // QT_NO_PROPERTIES
     return _id;
 }
 QT_END_MOC_NAMESPACE

There is absolutely nothing to prevent moc from just copying over the entire bar<1> statements into the QMetaObject::ReadProperty switch statements - but it somehow barfs on the <> template tags.


Solution

  • moc does not like template braces inside Q_PROPERTY declarations. You can do the following:

    class Foo : public QObject {
      Q_OBJECT
      Q_PROPERTY(int bar1 READ bar_1)
      Q_PROPERTY(int bar2 READ bar_2)
    
      private:
      inline int bar_1() const { return bar<1>(); }
      inline int bar_2() const { return bar<2>(); }
    
      public:
      template <int i> int bar() const;
    };
    

    The code generated by moc should be able to access private methods of your class, and this indirection should not induce any runtime cost since the compiler can inline the calls to bar_N.

    You probably want to hide the declaration of bar_N in a macro.