MetaObjectの使用によるQtでのシリアル化

背景


実際、なぜこれが必要なのでしょうか? 結局、C ++は既にストリームへのシリアル化のための非常に柔軟なオプションを提供しています。 しかし、プロジェクトで繰り返し使用するために、シリアライゼーション/デシリアライゼーションプロセスを最大限に普遍化するタスクがありました。

そのため、可能な限りQtで最も柔軟な(de)シリアル化システムを編成する必要がありました。
  1. 基本クラスから継承して展開する
  2. 別のシリアライザークラスを持っている
1つのコマンドでオブジェクトからデータのストリームを送信できる

同時に、何らかの方法で、オブジェクト内のどのデータをシリアル化する必要があり、どのデータを「スキップ」できる(およびする必要がある)かを示すことが必要でした。 同様に、逆シリアル化するときに、オブジェクト内のデータと関連する依存値を正しく設定する機能が可能になっているはずです。

解決策



ソリューションは、プロパティシステムの形で見つかりました。 Qtには、プロパティシステムを提供するC ++用のアドオンがあります。 Q_OBJECTタグを持つQObjectから継承したクラスでのみプロパティを使用する権利があります。 プロパティの割り当ては、ヘッダーでクラスを記述するときにこのタイプのマクロを使用するときに発生します(例)。
Q_PROPERTY(int Test READ readTest WRITE setTest)

この例からわかるように、読み取り(readTest)関数と書き込み(setTest)関数を割り当てることができます。 後者は、逆シリアル化が正しいことを保証します。

シリアル化に必要なパラメーターは、プロパティの形式でオブジェクトに入力されるため、クラスデータの整合性を確保するために必要なプロパティのリストを指定します。

一般的な方法でプロパティへのアクセスを提供するには(シリアル化に渡されたクラスに関する基本クラスが何も知らない場合)、MetaObjectシステム(オブジェクトをそのプロパティシステムで記述し、任意のオブジェクトに対して生成)MetaPropertyを使用して、説明を提供しますプロパティへのアクセス。 結果は、(de)シリアル化のコードです:
bool SerializedBase::Serialize(QDataStream *dataStream)
{
if (dataStream == NULL)
return false ;
for ( int i = 1; i < this ->metaObject()->propertyCount(); i++)
{
QMetaProperty prop = this ->metaObject()->property(i);
const char * propName = prop.name();
*dataStream << ( this ->property(propName));
}
return true ;
}

bool SerializedBase::DeSerialize(QDataStream *dataStream)
{
if (dataStream == NULL)
return false ;
for ( int i = 1; i < this ->metaObject()->propertyCount(); i++)
{
QMetaProperty prop = this ->metaObject()->property(i);
const char * propName = prop.name();
QVariant v;
*dataStream >> v;
this ->setProperty(propName, v);
}
return true ;
}


* This source code was highlighted with Source Code Highlighter .

その結果、1つのコマンドでオブジェクトをシリアライズ/デシリアライズできる(継承されたデータストリームがある場合)継承した基本クラスを取得しました。

独立したクラスのコードは、追加する点を除いて、ほぼ同じである必要があります。 目の前のオブジェクトまたは通常のデータ構造を理解するためのチェック)。

追加情報と修正



プロパティの形式のオブジェクトとそのシリアル化

QObjectクラスコードのQ_DISABLE_COPYディレクティブでは、オブジェクト値をプロパティに直接割り当てることはできません。 必要な柔軟性を提供するには、3つのアクションを実行する必要があります。
  1. コピーコンストラクターを作成します。 たとえば、オブジェクトがAAAの場合、コンストラクターは次のようになります(ヘッダー内): AAA(AAA* copy) 。 オブジェクトをコピーするときのアクションを実際に定義します。
  2. クラスヘッダーにメタクラスを登録します。 これは次のように行われますQ_DECRARE_METATYPE(AAA);
  3. 問題を「フラストレーション」 qRegisterMetaType("AAA")ないようにプロパティを使用する場合(通常はコンパイラレベルで定義されているため、見逃さないでください)、 qRegisterMetaType("AAA")コマンドを使用してアプリケーションのどこかにクラスを登録する必要があります。
    使用するストリーミングクラスを再割り当てする必要があります。つまり、それらをストリームに送信する方法を指定します。 これは、 qRegisterMetaTypeStreamOperators ( const char * typeName )ます。 クラスに実装する必要がある関数の説明:

    QDataStream &operator<<(QDataStream &out, const MyClass &myObj);
    QDataStream &operator>>(QDataStream &in, MyClass &myObj);


これらすべてのアクションの後、標準タイプの値だけでなく、オブジェクトプロパティを操作し、これらのタイプのタイプへの参照を使用できなくなります(欠点を参照)。

動的に作成されたインスタンスを使用する

プロパティのQtシステムはこれをサポートしますが、ここでも「直接」ではなく、身体の動きを実行する必要があります。 クラス(ベース)は上記のリストの要件を満たしている必要があります(項目4が既にオプションである場合を除き)。さらに、さらに( fuCtorの支援に感謝します):
1.使用するコンストラクターの説明にはQ_INVOKABLEマクロが必要です
2. QMetaObject :: newInstance()コマンドを使用して、オブジェクト(またはその識別子)QMetaObjectとともに渡すことができるクラス名にしがみつく必要があります。 MetaObjectを準備する方法がない場合は、 int QMetaType::type ( const char * typeName )介して登録済みタイプの名前Idで受信する前に、デフォルトコンストラクター(同じ要件を満たす必要があり、 QMetaType ::コンストラクトを使用する必要がありますint QMetaType::type ( const char * typeName ) Id(タイプが0と比較してシステムに登録されているかどうかを確認することを忘れないでください)MetaType自体。

長所と短所


この方法の利点を簡単に説明します。


しかし、現時点で特定されている欠点:

Source: https://habr.com/ru/post/J83374/


All Articles