動的なメタオブジェクト(パート1、研究)

まえがき



開発でQtを使用したすべての人が、メタ情報がどのように配置され、この美しいフレームワーク内で何が起こっているのかを知りたいと思いますか? これがこのレコードの目的です。ソースを調べて、動的メタオブジェクトの実装を記述しようとします(ただし、このレコードには含まれません)。 シグナルとスロットをリアルタイムで作成できるメタオブジェクト。



多くの人は、 すべてがすでに実装されていると言います (利用できない場合: Googleのキャッシュにあります )。 しかし、そのような実装では、 QObject::connect



作成できません。 そのような実装の値はゼロになる傾向があります。



少し勉強



したがって、 QObject



QObject



クラスのコンテンツを見てみましょう。 なんで? すべてのメタ情報クラスはQObject



子孫でなければならず、mocがメタ情報を生成するQ_OBJECT



マクロも持っている必要があります。



公式サイトからQtからコードをコピーします。 Qt 5.4を使用します。



したがって、クラス宣言自体は次のようになります。



QObjectクラスコード
 class Q_CORE_EXPORT QObjectData { public: virtual ~QObjectData() = 0; QObject *q_ptr; QObject *parent; QObjectList children; uint isWidget : 1; uint blockSig : 1; uint wasDeleted : 1; uint isDeletingChildren : 1; uint sendChildEvents : 1; uint receiveChildEvents : 1; uint isWindow : 1; //for QWindow uint unused : 25; int postedEvents; QDynamicMetaObjectData *metaObject; QMetaObject *dynamicMetaObject() const; }; class Q_CORE_EXPORT QObject { Q_OBJECT Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged) Q_DECLARE_PRIVATE(QObject) ///  protected: QScopedPointer<QObjectData> d_ptr; static const QMetaObject staticQtMetaObject; ///   }
      
      







同時に、単純なクラスAでプロジェクトを作成できます



 #include <QObject> class A : public QObject { Q_OBJECT public: explicit A(QObject *parent = 0); ~A(); signals: void signal(); public slots: void slot(){} };
      
      





しかし、これらすべての中で、メタオブジェクト自体とその構成要素に注意を払う必要があります。

MOCテキスト
 ///  QT_BEGIN_MOC_NAMESPACE struct qt_meta_stringdata_A_t { QByteArrayData data[4]; char stringdata[15]; }; #define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_A_t, stringdata) + ofs \ - idx * sizeof(QByteArrayData)) \ ) static const qt_meta_stringdata_A_t qt_meta_stringdata_A = { { QT_MOC_LITERAL(0, 0, 1), // "A" QT_MOC_LITERAL(1, 2, 6), // "signal" QT_MOC_LITERAL(2, 9, 0), // "" QT_MOC_LITERAL(3, 10, 4) // "slot" }, "A\0signal\0\0slot" }; #undef QT_MOC_LITERAL static const uint qt_meta_data_A[] = { // content: 7, // revision 0, // classname 0, 0, // classinfo 2, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 1, // signalCount // signals: name, argc, parameters, tag, flags 1, 0, 24, 2, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 3, 0, 25, 2, 0x0a /* Public */, // signals: parameters QMetaType::Void, // slots: parameters QMetaType::Void, 0 // eod }; void A::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { A *_t = static_cast<A *>(_o); switch (_id) { case 0: _t->signal(); break; case 1: _t->slot(); break; default: ; } } else if (_c == QMetaObject::IndexOfMethod) { ///        } Q_UNUSED(_a); } ///   !    ! const QMetaObject A::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_A.data, qt_meta_data_A, qt_static_metacall, Q_NULLPTR, Q_NULLPTR} }; const QMetaObject *A::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; } int A::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QObject::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < 2) qt_static_metacall(this, _c, _id, _a); _id -= 2; } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { if (_id < 2) *reinterpret_cast<int*>(_a[0]) = -1; _id -= 2; } return _id; } // SIGNAL 0 void A::signal() { QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR); } QT_END_MOC_NAMESPACE
      
      







したがって、これまで見てきたことから、いくつかの結論を導き出すことができます。動的メタQDynamicMetaObjectData * QObjectData::metaObject



動作時間QDynamicMetaObjectData * QObjectData::metaObject



QMetaObject * QObjectData::dynamicMetaObject() const



関数です。 したがって、Qtをどのように使用するのか、Qtをどのように使用するのかを学ぶ必要があります。



退屈なソースの読み取りをスキップして、すぐに言います。動的なメタオブジェクトを作成するためのクラスも残しました。



テキストq_object_p.h
 ///   struct QAbstractDynamicMetaObject; struct Q_CORE_EXPORT QDynamicMetaObjectData { virtual ~QDynamicMetaObjectData() {} virtual void objectDestroyed(QObject *) { delete this; } virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) = 0; ///    metaObject virtual int metaCall(QObject *, QMetaObject::Call, int _id, void **) = 0;///    //. }; ///     ,   . struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QDynamicMetaObjectData, public QMetaObject { virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) { return this; } virtual int createProperty(const char *, const char *) { return -1; }///    .    virtual int metaCall(QObject *, QMetaObject::Call c, int _id, void **a) { return metaCall(c, _id, a); } virtual int metaCall(QMetaObject::Call, int _id, void **) { return _id; } // Compat overload }; ///    
      
      







だから私たちから出てくるもの。 新しいメタオブジェクトを作成し、 QObject



子孫にあるQObject



しかし、あなたはあなたの信号とスロットのために場所を取ることができます。 一般的に、私たちの病気の想像力をサポートするすべてを行うために、私は信号とスロットの両方を追加できるメタオブジェクトの作成にもっと触発されたので、ここでそのようなメタオブジェクトを作成するための準備を強調します。



私たちは怠withと戦い、タスクに関する情報を収集します



したがって、メタオブジェクトを作成するには、メタオブジェクトの一般的な配置を確認する必要があります。 これを行うために、ソースに再び登り、これを見つけます:



メタオブジェクト構造
 struct Q_CORE_EXPORT QMetaObject { ///  struct { // private data const QMetaObject *superdata;///     const QByteArrayData *stringdata;///   (  ,  ,   ) const uint *data;///    (  ,  ,   ) typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); StaticMetacallFunction static_metacall;///  ,     . const QMetaObject * const *relatedMetaObjects;///,    void *extradata; //reserved for future use } d; }
      
      







ここから、およびMOCジェネレーターを使用したリストから、有効なメタオブジェクトには、 stringdata



data



2つの変数のみをdata



するか、 QMetaObject



クラスのすべての関数を完全に書き換える必要があることは明らかです。 2つの悪のうち、私はより少ないものを選択しました-このデータの検索はQtによって実行され、通常のメタオブジェクトより遅く見えないため、このデータを記入することにしました(はい、これは時期尚早の最適化です)。



始めるために、最も簡単なもの-文字列情報を見てみましょう。 MOCは、テストクラスAにこのコードを提供します。



文字列配列
 struct qt_meta_stringdata_A_t { QByteArrayData data[4]; char stringdata[15]; }; #define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_A_t, stringdata) + ofs \ - idx * sizeof(QByteArrayData)) \ ) static const qt_meta_stringdata_A_t qt_meta_stringdata_A = { { QT_MOC_LITERAL(0, 0, 1), // "A" QT_MOC_LITERAL(1, 2, 6), // "signal" QT_MOC_LITERAL(2, 9, 0), // "" QT_MOC_LITERAL(3, 10, 4) // "slot" }, "A\0signal\0\0slot" }; #undef QT_MOC_LITERAL
      
      







つまり 相対文字列参照を含むQByteArrayData



配列のみがあります( QByteArrayData



自体に対して)。 したがって、MOCのように、一緒にではなく、各行をメモリに簡単に個別に配置できます。



ここで、メインのメタ情報に移りましょう。ここでは、MOCが大きなuint配列を用意しました。



大きなuint配列
 static const uint qt_meta_data_A[] = { ///1  // content: 7, // revision 0, // classname 0, 0, // classinfo 2, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 1, // signalCount ///2  // signals: name, argc, parameters, tag, flags 1, 0, 24, 2, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 3, 0, 25, 2, 0x0a /* Public */, ///3  // signals: parameters QMetaType::Void, // slots: parameters QMetaType::Void, 0 // eod };
      
      







3つのブロックに分割します。 最初のブロックは、通常のQMetaObjectPrivate



クラスです。



QMetaObjectPrivateコア
 struct QMetaObjectPrivate { enum { OutputRevision = 7 }; // Used by moc, qmetaobjectbuilder and qdbus int revision; int className; int classInfoCount, classInfoData; int methodCount, methodData; int propertyCount, propertyData; int enumeratorCount, enumeratorData; int constructorCount, constructorData; //since revision 2 int flags; //since revision 3 int signalCount; //since revision 4 // revision 5 introduces changes in normalized signatures, no new members // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself // revision 7 is Qt 5 ///    ,      }
      
      







最初のブロックに等しい対応は難しくありません。 2番目のブロックはもう少し複雑です。 そこで構造の配列を取得します(Qtでは、そのような構造は記述されていません。これは非常に奇妙なので、独自のDataMethodInfo



)。



DataMethodInfo
 struct DataMethodInfo{ uint name;///    (    ) uint argsCount; ///   uint argOffset; /// offset    uint tag;/// ,   uint flags;///   private/protected/public    - ,     };
      
      







これですべてが明確になりました。 しかし、引数の説明ははるかに楽しいです。 最初にメソッドが返す型があり、ほとんどの場合QMetaType::Void



が発生します。 以下は、すべてのタイプの引数のリストです。 つまり、 QString testString (QString src, QString dst)



メソッドQString testString (QString src, QString dst)



場合、2 QMetaType :: QStringが存在します。 メソッドに引数がない場合、何も入力されません。 そして、引数のタイプをリストした後、これらの引数の名前のリストがあります。 したがって、 QString testString( QString src, QString dst )



メソッドQString testString( QString src, QString dst )



、メタデータコードは次のようになります。



 static const qt_meta_stringdata_A_t qt_meta_stringdata_A = { { QT_MOC_LITERAL(0, 0, 1), // "A" QT_MOC_LITERAL(1, 2, 6), // "signal" QT_MOC_LITERAL(2, 9, 0), // "" QT_MOC_LITERAL(3, 10, 4) // "slot" QT_MOC_LITERAL(4, 15, 10) // "testString" QT_MOC_LITERAL(5, 26, 3) // "src" QT_MOC_LITERAL(6, 30, 3) // "dst" }, "A\0signal\0\0slot\0testString\0src\dst" }; static const uint qt_meta_data_A[] = { ///1  // content: 7, // revision 0, // classname 0, 0, // classinfo 3, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 1, // signalCount ///2  // signals: name, argc, parameters, tag, flags 1, 0, 29, 2, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 3, 0, 30, 2, 0x0a /* Public */, 4, 2, 31, 2, 0x0a /* Public */, ///3  // signals: parameters QMetaType::Void, // slots: parameters QMetaType::Void, ////----------------------------------------------------------------- ///| return | Arguments Type | names | QMetaType::QString , QMetaType::QString, QMetaType::QString, 5 , 6 0 // eod };
      
      





引数のオフセットを計算するのは間違っているかもしれませんが、その意味は明らかだと思いますか? MOCの代わりにこのコードを挿入すると、クラスAメタオブジェクトにtestStringメソッドを追加できますが、機能しませんが、リストに表示されます。 そして、独自の一意のIDを持ちます。



データの一部からこれらすべてを生成するコードを記述するだけです。 興味がある場合は、次の号で、完全に機能する動的なメタオブジェクトを記述する方法を示します。



All Articles