Qt Q_PROPERTY с аксессорами шаблонов

Я стремлюсь к немного большему количеству повторного использования кода, сохраняя при этом многословие.

Рассмотрим следующий пример кода:

// 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 и т. д., но подпадающих под один и тот же «класс», — в этом случае я хотел бы иметь отдельные свойства, используя средства доступа с типом enum-templatetyped.

Раскомментирование определений свойств приводит к следующей ошибке 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_test.cpp из moc.h.

Чтобы проверить и продемонстрировать это дальше, я добавил еще одно свойство

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> в операторы switch QMetaObject::ReadProperty, но он каким-то образом ругается на теги шаблона ‹>.


person qdot    schedule 02.10.2012    source источник
comment
Насколько мне известно, moc запускается перед препроцессором C++ (чтобы заменить все ключевые слова Qt «настоящим» C++), поэтому код шаблона не расширяется, а moc не может этого сделать сам. Это то, на что ссылается ошибка синтаксического анализа.   -  person cmannett85    schedule 02.10.2012
comment
Основная проблема заключается в том, что moc использует собственный синтаксический анализатор, который распознает только подмножество C++.   -  person Stefan Majewsky    schedule 02.10.2012
comment
да, но мне интересно, как заставить его делать то же самое с кодом шаблона - я не думаю, что на этом этапе нужно расширять его - если вы замените "baz1" на "bar‹1›" в moc_test.cpp, это работает просто отлично.   -  person qdot    schedule 02.10.2012


Ответы (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 в макросе.

person Stefan Majewsky    schedule 02.10.2012
comment
как мне скрыть это объявление? - person qdot; 02.10.2012
comment
Я бы сделал что-то вроде #define BAR(N) inline int bar_##N() const { return bar<N>(); }, но ваше решение макроизации только bar<N> чище. - person Stefan Majewsky; 02.10.2012
comment
Ну, я бы сказал, что ваш чище - мой загрязняет глобальное пространство имен, так как нет абсолютно никакого DEFINE, который отличает компиляцию сгенерированного moc кода от компиляции "настоящего" кода. - person qdot; 02.10.2012

ХОРОШО. Кажется, я почти разобрался с этим. Это просто зло. Из-за загрязнения пространства имен - и, похоже, в 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

person qdot    schedule 02.10.2012

Просто поместите () вокруг функции чтения:

Q_PROPERTY(int bar1 READ (bar<1>))
person Broothy    schedule 16.02.2021