Qt Q_PROPERTY带有模板访问器

5
我希望您能在保持冗长的同时,提高代码重用性。以下是示例代码:
// 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;
}

这个程序按预期进行编译和运行。
如果你想知道为什么我想要这样做 - 想象一组属性,如DESIGNABLE等,但归属于同一个“类” - 在这种情况下,我希望使用枚举模板类型的访问器来获得单独的属性。
取消注释属性定义会导致以下 moc 错误:
/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"

有没有什么好的方法来正确地混合使用模板和moc?
为了回答cmannet85的评论并增加更多见解 - 是的,这个moc调用从moc.h生成moc_test.cpp。
为了进一步测试和演示它,我添加了另一个属性。
Q_PROPERTY(int baz1 READ baz1)

而moc_test.cpp在之前和之后的区别如下:

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

没有任何障碍阻止 moc(元对象编译器)将整个 bar <1> 语句复制到 QMetaObject::ReadProperty switch 语句中,但某种方式它在 <> 模板标签方面出现问题。


据我所知,moc 在 C++ 预处理器之前运行(用 'real' C++ 替换所有 Qt 关键字),因此模板代码不会被展开 - moc 本身也无法做到这一点。 这就是解析错误所指的。 - cmannett85
1
主要问题是 moc 使用自己的解析器,只能识别 C++ 的一个子集。 - Stefan Majewsky
是的,但我想知道如何对模板代码执行相同的操作 - 我不认为此步骤需要任何扩展 - 如果在moc_test.cpp中将'baz1'替换为'bar<1>',它可以正常工作。 - qdot
3个回答

3

moc 不支持在 Q_PROPERTY 声明中使用模板括号。您可以采取以下措施:

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;
};
moc生成的代码应该能够访问你的类的私有方法,并且这个间接性不会引入任何运行时成本,因为编译器可以将对bar_N的调用内联。
你可能希望使用宏来隐藏bar_N的声明。

我该如何隐藏那个声明? - qdot
我本来会这样做 #define BAR(N) inline int bar_##N() const { return bar<N>(); },但你只宏化 bar<N> 的解决方案更简洁。 - Stefan Majewsky
我会说你的更干净 - 我的会污染全局命名空间,因为没有任何定义可以区分 moc 生成的代码的编译和“真实”代码的编译。 - qdot

1

只需在读取函数周围加上括号:

Q_PROPERTY(int bar1 READ (bar<1>))

1

好的,我觉得我差不多弄清楚了。这只是由于命名空间污染而变得邪恶 - 在moc_test.cpp中似乎没有在包含我的头文件之前定义任何DEFINE。

仅仅定义

#define ___BAR1 bar<1> 
Q_PROPERTY(int bar1 READ ___BAR1) 

完成了工作 - 但我更喜欢___BAR1仅在MOC运行(很容易)和moc_test.cpp编译期间定义。

括号匹配

#ifdef Q_MOC_RUN
#define ___BAR1 bar<1> 
#endif Q_MOC_RUN

将定义隐藏在除 moc 以外的任何地方都是无关紧要的,因为当 gcc 编译 moc_test.cpp 时,它也会被隐藏。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接