使用宏来创建QObject派生类

时间:2015-09-16 15:03:38

标签: c++ qt macros c-preprocessor moc

我试图简化(即摆脱样板代码的负载)创建QObject包装类,转发其他QObject派生类的属性访问。

从小处开始,我只是尝试使用一个属性:

// Sy_test.h - The wrapped class
class Sy_test : public QObject
{
    Q_OBJECT
    Q_PROPERTY( bool prop READ getProp WRITE setProp NOTIFY propChanged )

public:
    Sy_test( QObject* parent = nullptr ) :
        QObject{ parent },
        prop_{ false } {}

    bool getProp() const { return prop_; }

public slots:
    void setProp( bool value )
    {
        if ( value != prop_ ) {
            prop_ = value;
            emit propChanged( prop_ );
        }
    }

signals:
    void propChanged( bool value );

private:
    bool prop_;
};

// Sy_proxy.h - The wrapper generator
#define SY_PROXYPROPERTY( Type, Name, Getter, Setter, Notifier )\
private:\
    Q_PROPERTY( Type Name READ Getter WRITE Setter NOTIFY Notifier )\
\
public:\
    Type Getter() const { return target_->Getter(); }\
\
public slots:\
    void Setter( Type value ) { target_->Setter( value ); }\
\
signals:\
    void Notifier( Type value );\
\
private:\
    void setConnection()\
    {\
        connect( target_, &std::remove_pointer< decltype( target_ ) >::type::Notifier,\
                 this,    &std::remove_pointer< decltype( this    ) >::type::Notifier );\
    }

#define SY_PROXY( ProxyName, TargetType, Prop1 )\
class ProxyName : public QObject\
{\
    Q_OBJECT \
    Prop1 \
\
public:\
    ProxyName( TargetType* target ) :\
        target_{ target }\
    {\
        setConnection();\
    }\
\
    virtual ~ProxyName() {}\
\
private:\
    TargetType* target_;\
};

// This should create a Sy_test wrapper class called Sy_testProxy
SY_PROXY( Sy_testProxy,
          Sy_test,
          SY_PROXYPROPERTY( bool, prop, getProp, setProp, propChanged ) )

因此SY_PROXY宏应该创建一个名为Sy_testProxy的类,它带有Sy_test::prop属性的副本,其实现仅转发请求/信号。

几乎。查看后预处理器输出(我使用g ++,以及.ii文件),我可以看到构建了Sy_testProxy类,它的形式与{{1}相同} .class。但是,我收到一个错误:

Sy_test

所以看起来../CppTest/Sy_proxy.h:47: Error: NOTIFY signal 'propChanged' of property 'prop' does not exist in class Sy_testProxy. make: *** [moc_Sy_proxy.cpp] Error 1 无法解析我的宏魔法;虽然我不确定moc宏存在的位置(错误来自名为SY_PROXY的类),Sy_testProxy也必须有效(如{ {1}}必须已从中读取SY_PROXYPROPERTY宏。谁能看到我出错的地方?

为了记录:我讨厌像其他人一样的宏,但由于moc厌恶模板和Q_PROPERTY虚拟继承,我已经投入使用它们。之所以触发此调查,是因为我在一个单独的线程中执行了大量计算的实例集合,但它们驱动了QML表示。但是,QML不允​​许连接/属性绑定到主线程之外的对象,因此我被迫创建了主线程中的代理对象。如果有人有更好的想法,我对他们非常开放!

3 个答案:

答案 0 :(得分:4)

moc不太喜欢宏。它在某种程度上扩展了它们,但是当它们变得复杂时就会失败¹。

您可以尝试将signals:替换为public:²(即手动展开signals宏),并通过放置{{1}来设置您希望函数成为信号的moc在函数声明前面。

<强>替换

Q_SIGNAL

<强>与

signals:\
    void Notifier( Type value );\

<子> ¹:对于复杂的某些定义......我不知道它何时失败,但我在过去遇到了一些不同的问题。根据我的经验,我的猜测是当宏体包含另一个宏时moc有问题,例如你的例子中的public:\ Q_SIGNAL void Notifier( Type value );\ 。但这只是一个猜测 - 也许moc失败的那种宏是别的东西。

<子> ²:在Qt 5之前,曾经是signals

答案 1 :(得分:0)

除了moc的变幻莫测之外,你的包装器不是线程安全的。不从正确的线程调用属性getter。所以我没有看到包装器的任何意义。您也可以直接从QML使用包装类,而不是包装器。

为了保证线程安全,您的包装器应该缓存包装属性的值,以便始终从本地副本进行读取。

此时您还可以编写一个完全动态的包装器,该包装器可以从包装对象中线程安全地转发所有属性。使用元对象系统,您可以动态生成所有内容 - 属性值的副本等。就属性而言,您可以复制整个二进制描述符,因为您的包装器假装具有相同的属性。

答案 2 :(得分:0)

有一段优秀的代码,你可以google for qmltricks,它拥有你需要的一切,作为一个良好的开端。

您只需要一个标题。有一个扩展的空间,以支持只读属性或自定义getter / setter ..但我建议你看看。我现在找不到original.page,在最后一次Qt峰会上看到了一个演示文稿,你可以查看qt网站上的动手资料。

在github上的链接下面,有几个版本可用。

https://github.com/Cavewhere/lib-qt-qml-tricks/blob/master/include/QQmlHelpers