Qt リモートオブジェクトコンパイラ

REPC の概要

Replica Compiler(repc) は、API 定義ファイルに基づいてQObject のヘッダーファイルを生成します。このファイル("rep "ファイルと呼ばれます)は、APIを記述するために特定の(テキスト)構文を使用します。慣例として、これらのファイルにはReplicaの略である.repファイル拡張子が与えられます。これらのファイルがrepcによって処理されると、repcはSourceと Replicaの両方のヘッダーファイルを生成します。

Qt Remote Objects モジュールにはCMake 関数と qmake 変数も含まれており、プロジェクトファイルに追加することで repc を自動的に実行し、ビルドプロセス中にMeta Object Compilerによって処理されるファイルリストに結果のファイルを追加することができます。

Qt Remote Objects は、QObject をネットワーク経由で共有することをサポートしていますが(Source 側で enableRemoting を使用し、Replica 側で acquireDynamic を使用します)、repc にオブジェクトの定義を任せることにはいくつかの利点があります。まず、DynamicReplicas は便利ですが、扱いが面倒です。APIはオブジェクトが初期化されるまでわかりませんし、C++からAPIを使用するにはQMetaObject'sのメソッドで文字列を検索する必要があります。第二に、コンパイル時にインターフェイスが分かっていることで、コンパイル時と実行時の問題を見つけることができる。第三に、rep フォーマットはデフォルト値をサポートしており、Replica のインスタンス化時にソースが利用可能であることを確認できない場合に便利です。

生成されたファイルをコードで使用する方法については、こちらのドキュメントを参照してください。ここでは repc フォーマットとオプションについて説明します。

rep ファイル形式

rep ファイルフォーマットは、Qt Remote Objects (QtRO) でサポートされるインターフェイスを記述するためのシンプルなDSL (Domain Specific Language)です。QtROはオブジェクトベースのシステムなので、これらのインターフェイスはオブジェクト、つまりプロパティ、シグナル、スロットを持つクラスを通して利用可能なAPIによって定義されます。

クラスの種類

repファイルで定義された各クラスは、生成されたヘッダーファイルのQObject 、記述されたAPIが生成されます。

クラスを定義するには、class キーワードを使用し、その後に型に必要な名前を付け、API を次のように括弧で囲みます。

class MyType
{
    //PROP/CLASS/MODEL/SIGNAL/SLOT/ENUM declarations to define your API
};

生成されたヘッダーファイルをライブラリ内で使用する場合、シンボルの可視性を設定するためにクラス属性を定義する必要があるかもしれません。これは、class キーワードの後に属性を定義することで、C++ と同様に行うことができます。以下の例では、"mysharedlib_global.h" で定義されているMYSHAREDLIB_EXPORT マクロを使用しています。共有ライブラリの作成」を参照してください。

#include "mysharedlib_global.h"
class MYSHAREDLIB_EXPORT MyType
{
    ...
};

PROP

Q_PROPERTY 要素は、rep ファイルで PROP キーワードを使用して作成します。構文は、 キーワードの後に括弧で囲まれた定義が続くもので、定義は型、名前、(オプションで)デフォルト値や属性です。PROP

PROP(bool simpleBool)                // boolean named simpleBool
PROP(bool defaultFalseBool=false)    // boolean named defaultFalseBool, with false
                                     // as the default value

PROP(int lifeUniverseEverything=42)  // int value that defaults to 42
PROP(QByteArray myBinaryInfo)        // Qt types are fine, may need #include
                                     // additional headers in your rep file

PROP(QString name CONSTANT)          // Property with the CONSTANT attribute
PROP(QString setable READWRITE)      // Property with the READWRITE attribute
                                     // note: Properties default to READPUSH
                                     // (see description below)

PROP(SomeOtherType myCustomType)     // Custom types work. Needs #include for the
                                     // appropriate header for your type, make
                                     // sure your type is known to the metabject
                                     // system, and make sure it supports Queued
                                     // Connections (see Q_DECLARE_METATYPE and
                                     // qRegisterMetaType)

カスタム・タイプの作成に関する詳細は、こちらを参照してください。

デフォルトでは、プロパティにはゲッターと "push "スロットが定義され、値が変更されたときにnotifyシグナルが発行されます。Qt Remote Objects では、Source オブジェクトの notify シグナルが、アタッチされた Replicas に更新を送信するトリガーとして必要です。QtROの以前のバージョンでは、プロパティはデフォルトで読み取り/書き込み、つまりゲッターとセッターを持っていました。しかし、QtRO の非同期的な性質により、直感的でない動作をすることがありました。PROPにREADWRITE属性を設定することで、以前の(ゲッターとセッターの)動作を提供します。

// In .rep file, old (setter) behavior
PROP(int myVal READWRITE)             // Old behavior with setMyVal(int myVal) method

// In code...  Assume myVal is initially set to 0 in Source
int originalValue = rep->myVal();     // Will be 0
rep->setMyVal(10);                    // Call setter, expecting a blocking/
                                      // non-asynchronous return

if (rep->myVal() == 10) ...           // Test will usually fail

値が変更されるまでブロックする必要がある場合は、以下のようなものが必要です。

// In .rep file, old (setter) behavior
PROP(int myVal READWRITE)             // Old behavior with setMyVal(int myVal) method

// In code...  Assume myVal is initially set to 0 in Source
bool originalValue = rep->myVal();    // Will be 0

// We can wait for the change using \l QSignalSpy
QSignalSpy spy(rep, SIGNAL(myValChanged(int)));

rep->setMyVal(10);                    // Call setter, expecting a blocking/
                                      // non-asynchronous return

spy.wait();                           // spy.wait() blocks until changed signal
                                      // is received
if (rep->myVal() == 10) ...           // Test will succeed assuming
                                      // 1. Source object is connected
                                      // 2. Nobody else (Source or other Replica)
                                      //    sets the myVal to something else (race
                                      //    condition)
// Rather than use QSignalSpy, the event-driven practice would be to connect the
// myValChanged notify signal to a slot that responds to the changes.

QtROのデフォルトはREADPUSHになり、プロパティ変更を要求するための自動生成スロットが提供されます。

// In .rep file, defaults to READPUSH
PROP(bool myVal)                      // No setMyVal(int myVal) on Replica, has
                                      // pushMyVal(int myVal) instead

// In code...  Assume myVal is initially set to 0 in Source
bool originalValue = rep->myVal();    // Will be 0

// We can wait for the change using \l QSignalSpy
QSignalSpy spy(rep, SIGNAL(myValChanged(int)));

rep->pushMyVal(10);                   // Call push method, no expectation that change
                                      // is applied upon method completion.

// Some way of waiting for change to be received by the Replica is still necessary,
// but hopefully not a surprise with the new pushMyVal() Slot.
spy.wait();                           // spy.wait() blocks until changed signal
                                      // is received
if (rep->myVal() == 10) ...           // Test will succeed assuming
                                      // 1. Source object is connected
                                      // 2. Nobody else (Source or other Replica)
                                      //    set the myVal to something else (race
                                      //    condition)

PROP 宣言でCONSTANT,READONLY,PERSISTED,READWRITE,READPUSH,SOURCEONLYSETTER キーワードを使用することもでき、プロパティの実装方法に影響を与えます。READPUSHは、値が使われていない場合のデフォルト値です。

PROP(int lifeUniverseEverything=42 CONSTANT)
PROP(QString name READONLY)

ここでいくつか微妙な点があることに注意してください。CONSTANT PROPは、ソース側でCONSTANTとして宣言されたQ_PROPERTY 。しかし、レプリカは初期化されるまで正しい値を知ることができません。つまり、プロパティ値は初期化中に変更できるようにしなければなりません。READONLYの場合、ソース側にはセッターもプッシュ・スロットもなく、レプリカ側にはプッシュ・スロットは生成されません。PROP に PERSISTED 特性を追加すると、PROP は(もしあれば)Node に設定されたQRemoteObjectAbstractPersistedStore インスタンスを使用して PROP の値を保存/復元します。

別のニュアンスの値として SOURCEONLYSETTER があります。これは非対称な振る舞いを指定する別の方法を提供し、Source(具体的にはヘルパークラスであるSimpleSource )はプロパティの public ゲッターとセッターを持ちますが、Replica側では ReadOnly(通知シグナル付き)になります。そのため、ソース側からはプロパティを完全に制御できますが、Replica側からは観測することしかできません。SOURCEONLYSETTER は、MODEL と CLASS インスタンスに対して repc が使用するモードです。つまり、ソースはポイントされたオブジェクトを変更することができますが、set<Prop> メソッドや push<Prop> メソッドが生成されないため、Replicaは新しいオブジェクトを提供することができません。これは、ポインタ自体を変更できるだけで、ポイントされた型のプロパティの動作には影響しないことに注意してください。

CLASS

CLASSキーワードは、QObject から派生したオブジェクトのための特別なQ_PROPERTY 要素を生成します。これらのプロパティは、SOURCEONLYSETTERと同じセマンティクスを持ちます。構文は、CLASS キーワードの後にプロパティ名を続け、その後に括弧で囲んだサブオブジェクトの型を続けます。

// In .rep file
class OtherClass
{
    PROP(int value)
}

class MainClass
{
    CLASS subObject(OtherClass)
}

MODEL

MODEL キーワードは、QAbstractItemModel から派生したオブジェクトのための特別なQ_PROPERTY 要素を生成します。これらのプロパティは、SOURCEONLYSETTER と同じセマンティクスを持ちます。構文は、MODEL キーワードの後にプロパティ名を続け、その後にレプリカに公開されるべき (カンマで区切られた) ロールを括弧で囲みます。

// In .rep file
class CdClass
{
    PROP(QString title READONLY)
    MODEL tracks(title, artist, length)
}

シグナル

シグナル・メソッドは、rep ファイルで SIGNAL キーワードを使用して作成します。

使い方は、SIGNAL を宣言し、その後に必要なシグネチャを括弧で囲みます。戻り値のvoidは省略します。

SIGNAL(test())
SIGNAL(test(QString foo, int bar))
SIGNAL(test(QMap<QString,int> foo))
SIGNAL(test(const QString &foo))
SIGNAL(test(QString &foo))

Qtqueued connections のように、参照であるシグナルのパラメータは、レプリカに渡されるときにコピーされます。

スロット

スロット・メソッドは、rep ファイルで SLOT キーワードを使用して作成します。

使い方は、SLOT を宣言し、その後に必要なシグネチャを括弧で囲みます。戻り値は宣言に含めることができます。戻り値を省略した場合、生成されるファイルでは void が使用されます。

SLOT(test())
SLOT(void test(QString foo, int bar))
SLOT(test(QMap<QString,int> foo))
SLOT(test(QMap<QString,int> foo, QMap<QString,int> bar))
SLOT(test(QMap<QList<QString>,int> foo))
SLOT(test(const QString &foo))
SLOT(test(QString &foo))
SLOT(test(const QMap<QList<QString>,int> &foo))
SLOT(test(const QString &foo, int bar))

Qtqueued connections や QtRO SIGNALS と同様に、参照であるスロットのパラメータはレプリカに渡される際にコピーされます。

ENUM

列挙(C++ の enum と QtRO のQ_ENUM を組み合わせたもの)は、ENUM キーワードを使って記述します。

ENUM MyEnum {Foo}
ENUM MyEnum {Foo, Bar}
ENUM MyEnum {Foo, Bar = -1}
ENUM MyEnum {Foo=-1, Bar}
ENUM MyEnum {Foo=0xf, Bar}
ENUM MyEnum {Foo=1, Bar=3, Bas=5}

関連トピックENUM 型,USE_ENUM キーワード

POD型

POD(Plain Old Data)とは、C++の構造体のような単純なデータ・コレクションを表す用語です。例えば、電話帳の API がある場合、そのインタフェースで "address" という概念を使用したいと思うかもしれません (address には street、city、state、country、postal code が含まれるかもしれません)。PODキーワードを使ってこのようなオブジェクトを定義し、クラス定義のPROP/SIGNAL/SLOT定義で使用することができます。

使い方は、POD の後に生成される型の名前を宣言し、その後に型と名前のペアをカンマで区切って宣言し、型と名前のペアを括弧で囲みます。

POD Foo(int bar)
POD Foo(int bar, double bas)
POD Foo(QMap<QString,int> bar)
POD Foo(QList<QString> bar)
POD Foo(QMap<QString,int> bar, QMap<double,int> bas)

完全な例は次のようになる。

POD Foo(QList<QString> bar)
class MyType
{
    SIGNAL(sendCustom(Foo foo));
};

repcによって生成されたコードは、各PODに対応するQ_GADGET クラスを作成し、PODに定義された各型に対応するQ_PROPERTY メンバを作成します。

生成されたヘッダーファイルをライブラリ内で使用する場合、シンボルの可視性を設定するためにクラス属性を定義する必要があるかもしれません。これは、POD キーワードの後に属性を定義することで行うことができます。以下の例では、"mysharedlib_global.h" で定義されているMYSHAREDLIB_EXPORT マクロを使用しています。この動作の詳細については、「共有ライブラリの作成」を参照してください。

#include "mysharedlib_global.h"
POD MYSHAREDLIB_EXPORT Foo(int bar)

ENUM型

多くの場合、ENUM はクラスの内部で定義する方が簡単ですっきりしています(ENUM を参照)が、独立した列挙型が必要な場合は、クラス定義の外部で ENUM キーワードを使用すると便利です。これにより、マーシャリングなどを処理する新しいクラスがヘッダーファイル内に生成されます。構文はENUM と同じですが、この場合の宣言はclass 宣言に含まれないという例外があります。

関連トピックENUM,USE_ENUM キーワード

USE_ENUM キーワード

USE_ENUM キーワードは、ENUM キーワードによる自動生成が追加される前に実装されていました。後方互換性のために残されています。

関連するトピックENUM,ENUM 型

ディレクティブ

rep ファイルはインターフェースを定義しますが、インターフェースはしばしば外部要素を必要とします。これをサポートするために、repc は生成されるファイルの先頭に任意の(1行の)ディレクティブを含めます。これにより、例えば、必要なロジックやデータ型をサポートする#includeや#defineディレクティブを使用することができます。

repcツールは現在、"#"シンボルから行末までのすべてを無視し、生成されたファイルに追加します。そのため、複数行の#if/#else/#endif文や複数行のマクロはサポートされていません。

#HEADER#FOOTER という2つの特別なディレクティブがあります。これらのディレクティブは、インターフェイス宣言の前(HEADER)か後(FOOTER)に、生成されたコードにそのまま入れるべき内容を定義するために使うことができます。先頭の#HEADER#FOOTER のトークンと空白1文字が取り除かれます。

以下の例では、生成された repc クラスは名前空間の中に置かれます。

#HEADER namespace MyNamespace {
class MyType
{
    ...
};
#FOOTER } // namespace MyNamespace

CMake関数

ソース・タイプとレプリカ・タイプを生成するための CMake 関数を以下に示します。

qt_add_repc_merged

Qt Remote Objects .rep ファイルから、ソース・タイプとレプリカ・タイプの C++ ヘッダー・ファイルを生成します。

qt_add_repc_replicas

Qt Remote Objects .rep ファイルからレプリカ型の C++ ヘッダーファイルを作成します。

qt_add_repc_sources

Qt Remote Objects .rep ファイルからソース タイプ用の C++ ヘッダー ファイルを作成します。

qt_reps_from_headers

QObject ヘッダーファイルから .rep ファイルを作成します。

qmake変数

REPC_REPLICA

レプリカ・ヘッダー・ファイルの生成に使用する、プロジェクト内のすべてのrepファイルの名前を指定します。

例えば

REPC_REPLICA = media.rep \
               location.rep

生成されるファイルはrep_<replica file base>_replica.h という形式になります。

REPC_SOURCE

ソース・ヘッダ・ファイルの生成に使用する、プロジェクト内のすべての rep ファイルの名前を指定します。

例えば

REPC_SOURCE = media.rep \
              location.rep

生成されるファイルはrep_<replica file base>_source.h という形式になります。

REPC_MERGED

複合(ソースとレプリカ)ヘッダー・ファイルの生成に使用する、プロジェクト内のすべての rep ファイルの名前を指定します。

例えば

REPC_MERGED = media.rep \
              location.rep

生成されるファイルはrep_<replica file base>_merged.h という形式になります。

注意: 通常、ソースとレプリカは別々のプロセスまたはデバイスに存在するため、この変数はあまり使用されません。

QOBJECT_REP

対応する.repファイルを生成するために使用されるべき、既存のQObject ヘッダーファイルの名前を指定します。

QRemoteObjectAbstractPersistedStoreも参照してください

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。