简体   繁体   English

使用宏创建QObject派生类

[英]Using a macro to create QObject derived classes

I'm trying to simplify (ie get rid of loads of boilerplate code) the creation of QObject wrapper classes that forward property access of other QObject derived classes. 我试图简化(即摆脱样板代码的负担)创建QObject包装器类, QObject转发其他QObject派生类的属性访问。

To start small, I'm just trying it with one property: 首先,我只是尝试使用一种属性:

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

So the SY_PROXY macro should create a class called Sy_testProxy that carries a copy of the Sy_test::prop property with implementations that just forward requests/signals. 因此, SY_PROXY宏应创建一个名为Sy_testProxy的类,该类携带Sy_test::prop属性的副本以及仅转发请求/信号的实现。

And it almost does. 而且几乎可以做到。 Looking at the post-preprocessor output (I'm using g++, so the .ii files), I can see the Sy_testProxy class is built and it's of the same form as the Sy_test class. 查看后预处理器的输出(我使用g ++,所以使用.ii文件),我可以看到Sy_testProxy类已构建,并且其形式与Sy_test类相同。 However, I get an error: 但是,我得到一个错误:

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

So it looks like the moc is failing to parse my macro magic; 这样看来, moc无法解析我的宏魔术。 although I'm not sure where as clearly the SY_PROXY macro is present (the error is coming from a class called Sy_testProxy ), and SY_PROXYPROPERTY must be valid too (as the moc must have read the Q_PROPERTY macro from it). 尽管我不确定SY_PROXY宏在哪里存在(错误来自名为Sy_testProxy的类),并且SY_PROXYPROPERTY必须有效(因为moc必须从中读取Q_PROPERTY宏)。 Can anyone see where I've gone wrong? 谁能看到我哪里出问题了?

For the record: I hate macros like everyone else, but I've fell into using them due to the moc 's aversion to templates and QObject virtual inheritance. 记录一下:我讨厌像其他所有人一样的宏,但是由于moc对模板和QObject虚拟继承的厌恶,我不得不使用它们。 This investigation was triggered because I had a collection of instances performing heavy calculations in a separate thread, but they drive QML representations. 之所以触发此调查,是因为我有一个在单独的线程中执行大量计算的实例的集合,但是它们驱动QML表示形式。 However QML does not allow connections/property bindings to objects outside of the main thread, so I've been forced into creating proxy object that live in the main thread. 但是QML不允​​许连接/属性绑定到主线程之外的对象,因此我被迫创建驻留在主线程中的代理对象。 If anyone has a better idea, I'm very open to them! 如果有人有更好的主意,我将非常欢迎他们!

moc doesn't like macros very well. moc不太喜欢宏。 It expands them to some degree, but it fails when they get complicated¹. 它可以在一定程度上扩展它们,但是当它们变得复杂¹时会失败。

You can try to replace signals: with public: ² (ie manually expanding the signals macro), andtell moc that you want the function to be a signal by putting Q_SIGNAL in front of the function declaration. 您可以尝试将signals:替换为public: ²(即,手动扩展signals宏),并通过将Q_SIGNAL放在函数声明的前面,告诉您希望该函数成为信号。

Replace 更换

signals:\
    void Notifier( Type value );\

with

public:\
    Q_SIGNAL void Notifier( Type value );\

¹: for some definition of complicated ... I don't know when it fails, but I ran into some different problems in the past. ¹:关于复杂的定义...我不知道什么时候失败,但是过去我遇到了一些不同的问题。 From my experience my guess is that moc has problems when a macro body contains another macro, like signals in your example. 根据我的经验,当宏主体包含另一个宏时,moc会出现问题,例如您的示例中的signals But this is just a guess - maybe the kind of macros moc fails at is something else. 但这只是一个猜测-也许moc失败的那种宏是另外一回事。

²: Before Qt 5, that used to be protected . ²:在Qt 5之前,它曾经是protected

The vagaries of moc aside, your wrapper is not thread-safe. 除了杂乱无章的moc ,您的包装也不是线程安全的。 The property getter is not invoked from the correct thread. 没有从正确的线程调用属性getter。 So I don't see any point of the wrapper. 所以我看不到包装的任何地方。 You might as well use the wrapped class directly from QML, not the wrapper. 您最好直接从QML中使用包装的类,而不是包装器。

To be thread-safe, your wrapper should be caching the wrapped property's value, so that the reads always happen from the local copy. 为了线程安全,包装程序应缓存已包装属性的值,以便始终从本地副本进行读取。

At that point you might as well write a fully dynamic wrapper that thread-safely forwards all properties from the wrapped object. 到那时,您还可以编写一个完全动态的包装器,该包装器线程安全地转发包装对象中的所有属性。 Using the metaobject system you can generate everything on the fly - the copies of the property values, etc. As far as properties go, you can copy the entire binary descriptor as your wrapper pretends to have the same properties. 使用元对象系统,您可以即时生成所有内容-属性值的副本,等等。就属性而言,您可以复制整个二进制描述符,就像包装器假装具有相同的属性一样。

There is an excellent piece of code, you can google for qmltricks, it has everything you need as a good start. 有一段很棒的代码,您可以在Google上搜索qmltricks,它提供了所需的一切作为一个好的开始。

You will need just one header. 您只需要一个标题。 There is a room for extend to support read only properties or custom getters/setters.. but I would suggest to hae a look. 有扩展空间以支持只读属性或自定义getter / setters ..但我建议您看看。 I cant find an original.page now, saw a presentation on last Qt Summit, you probably can check qt site for hands-on materials. 我现在找不到原始页面,在上一届Qt峰会上看到一个演讲,您可能可以在qt网站上查看动手材料。

Below a link on a github, there are several versions around available. 在github上的链接下面,有几个可用的版本。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM