Using Macros to Create Derived QObject Classes

I am trying to simplify (i.e. get rid of the loads of template code) creating QObject wrapper classes that redirect access to the properties of other derived QObject classes.

To start small, I'm just trying to use 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 send requests / signals.

And it is almost so. Looking at the output of the postprocessor (I use g ++, so the .ii files), I see that the Sy_testProxy class Sy_testProxy built and has the same shape as the Sy_test class. However, I get the error message:

 ../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 seems that moc not parsing my macromagy; although I'm not sure how to clearly present macro SY_PROXY (the error comes from a class called Sy_testProxy ), as well as SY_PROXYPROPERTY (since moc must read Q_PROPERTY macro from it). Can anyone see where I made a mistake?

For the record: I hate macros, like everyone else, but I ended up using them because of moc aversion to templates and QObject virtual inheritance. This study was initiated because I had a collection of instances performing heavy computations in a separate thread, but they controlled the QML views. However, QML does not allow binding of links / properties to objects outside the main thread, so I was forced to create a proxy object that lives in the main thread. If anyone has a better idea, I am very open to them!

+5
source share
3 answers

moc doesn't really like macros. He expands them to some extent, but he fails when they become complex.

You can try replacing signals: with public: ² (i.e., manually expand the signals macro) and use moc so that the function is a signal by placing Q_SIGNAL before the function declaration.

Replace

 signals:\ void Notifier( Type value );\ 

with

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

<sub> No.: for some definition of complex ... I don't know when it fails, but in the past I ran into some different problems. In my experience, my hunch is that moc has problems when a macro contains another macro, for example, signals in your example. But this is just an assumption - maybe the moc macro is not working, this is something else. Sub>

<sub>²: Before Qt 5, it was protected . Sub>

+4
source

Whims of moc to the side, your wrapper is not thread safe. The getter property is not called from the correct thread. Therefore, I do not see the point in the wrapper. You can also use a wrapped class directly from QML, rather than with a wrapper.

To be thread safe, your wrapper must cache the value of the wrapped property so that reads always come from the local copy.

At this point, you can also write a fully dynamic wrapper that safely transfers all the properties from the wrapped object. Using a system of meta objects, you can generate everything on the fly - copies of property values, etc. Regarding properties, you can copy the entire binary descriptor, as your wrapper pretends to have the same properties.

0
source

There is a great piece of code, you can google for qmltricks, it has everything you need as a good start.

You will need only one heading. There is room for expansion to support read-only properties or custom getters / setters. But I would suggest a look. I can’t find the original page now, after seeing the presentation at the last Qt Summit, you can probably check the qt website for practical materials.

Below are several github links available.

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

0
source

All Articles