簡體   English   中英

使用宏創建QObject派生類

[英]Using a macro to create QObject derived classes

我試圖簡化(即擺脫樣板代碼的負擔)創建QObject包裝器類, 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類已構建,並且其形式與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的類),並且SY_PROXYPROPERTY必須有效(因為moc必須從中讀取Q_PROPERTY宏)。 誰能看到我哪里出問題了?

記錄一下:我討厭像其他所有人一樣的宏,但是由於moc對模板和QObject虛擬繼承的厭惡,我不得不使用它們。 之所以觸發此調查,是因為我有一個在單獨的線程中執行大量計算的實例的集合,但是它們驅動QML表示形式。 但是QML不允​​許連接/屬性綁定到主線程之外的對象,因此我被迫創建駐留在主線程中的代理對象。 如果有人有更好的主意,我將非常歡迎他們!

moc不太喜歡宏。 它可以在一定程度上擴展它們,但是當它們變得復雜¹時會失敗。

您可以嘗試將signals:替換為public: ²(即,手動擴展signals宏),並通過將Q_SIGNAL放在函數聲明的前面,告訴您希望該函數成為信號。

更換

signals:\
    void Notifier( Type value );\

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

¹:關於復雜的定義...我不知道什么時候失敗,但是過去我遇到了一些不同的問題。 根據我的經驗,當宏主體包含另一個宏時,moc會出現問題,例如您的示例中的signals 但這只是一個猜測-也許moc失敗的那種宏是另外一回事。

²:在Qt 5之前,它曾經是protected

除了雜亂無章的moc ,您的包裝也不是線程安全的。 沒有從正確的線程調用屬性getter。 所以我看不到包裝的任何地方。 您最好直接從QML中使用包裝的類,而不是包裝器。

為了線程安全,包裝程序應緩存已包裝屬性的值,以便始終從本地副本進行讀取。

到那時,您還可以編寫一個完全動態的包裝器,該包裝器線程安全地轉發包裝對象中的所有屬性。 使用元對象系統,您可以即時生成所有內容-屬性值的副本,等等。就屬性而言,您可以復制整個二進制描述符,就像包裝器假裝具有相同的屬性一樣。

有一段很棒的代碼,您可以在Google上搜索qmltricks,它提供了所需的一切作為一個好的開始。

您只需要一個標題。 有擴展空間以支持只讀屬性或自定義getter / setters ..但我建議您看看。 我現在找不到原始頁面,在上一屆Qt峰會上看到一個演講,您可能可以在qt網站上查看動手材料。

在github上的鏈接下面,有幾個可用的版本。

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