Qt Remote Objects Compiler
REPC Überblick
Der Replica Compiler(repc) generiert QObject Header-Dateien auf der Grundlage einer API-Definitionsdatei. Die Datei (eine so genannte "rep"-Datei) verwendet eine spezifische (Text-)Syntax zur Beschreibung der API. Konventionell erhalten diese Dateien die Dateierweiterung .rep, kurz für Replica. Wenn diese Dateien von repc verarbeitet werden, erzeugt repc sowohl Source- als auch Replica-Header-Dateien.
Das Modul Qt Remote Objects enthält auch CMake-Funktionen und qmake-Variablen, die zu Ihrer Projektdatei hinzugefügt werden können, um repc automatisch auszuführen und die resultierenden Dateien zur Liste der Dateien hinzuzufügen, die vom Meta-Object Compiler während des Build-Prozesses verarbeitet werden, was die Verwendung von Qt Remote Objects in Ihren Projekten einfach macht.
Während Qt Remote Objects die gemeinsame Nutzung von QObject über das Netzwerk unterstützt (mit enableRemoting auf der Source-Seite und acquireDynamic auf der Replica-Seite), gibt es einige Vorteile, wenn Sie repc Ihre Objekte definieren lassen. Zunächst einmal ist DynamicReplicas zwar nützlich, aber auch umständlicher in der Handhabung. Die API ist nicht bekannt, bis das Objekt initialisiert ist, und die Verwendung der API von C++ aus erfordert String-Lookups durch die Methoden von QMetaObject. Zweitens ist die Schnittstelle bereits zur Kompilierzeit bekannt, so dass Probleme bei der Kompilierung und nicht erst zur Laufzeit auftreten. Drittens unterstützt das Replica-Format Standardwerte, was praktisch sein kann, wenn Sie nicht sicherstellen können, dass die Quelle verfügbar ist, wenn das Replica instanziert wird.
In der Dokumentation finden Sie Informationen zur Verwendung der generierten Dateien in Ihrem Code. Hier werden wir uns auf das repc-Format und die Optionen konzentrieren.
Das rep-Dateiformat
Das rep-Dateiformat ist eine einfache domänenspezifische Sprache (DSL) zur Beschreibung einer Schnittstelle, die über Qt Remote Objects (QtRO) unterstützt wird. Da QtRO ein objektbasiertes System ist, werden diese Schnittstellen durch APIs definiert, die über Objekte verfügbar sind, d.h. Klassen mit Eigenschaften, Signalen und Slots.
Der Klassentyp
Jede Klasse, die in einer Rep-Datei definiert ist, wird in den generierten Header-Dateien zu einer QObject, wobei die beschriebene API für Sie generiert wird.
Um eine Klasse zu definieren, verwenden Sie das Schlüsselwort class
, gefolgt von dem Namen, den Sie für Ihren Typ verwenden möchten, und schließen Sie dann Ihre API in Klammern ein, wie folgt
class MyType { //PROP/CLASS/MODEL/SIGNAL/SLOT/ENUM declarations to define your API };
Wenn Sie die generierten Header-Dateien innerhalb einer Bibliothek verwenden, kann es erforderlich sein, Klassenattribute zu definieren, um die Sichtbarkeit von Symbolen festzulegen. Dies kann ähnlich wie in C++ geschehen, indem das Attribut nach dem Schlüsselwort class
definiert wird. Im folgenden Beispiel wird das Makro MYSHAREDLIB_EXPORT
verwendet, das in "mysharedlib_global.h"
definiert ist. Weitere Informationen zur Funktionsweise finden Sie unter Erstellen gemeinsamer Bibliotheken.
#include "mysharedlib_global.h" class MYSHAREDLIB_EXPORT MyType { ... };
PROP
Q_PROPERTY Elemente werden durch Verwendung des PROP-Schlüsselworts in der rep-Datei erstellt. Die Syntax ist das Schlüsselwort PROP
, gefolgt von der in Klammern eingeschlossenen Definition, wobei die Definition aus dem Typ, dem Namen und (optional) einem Standardwert oder Attributen besteht.
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)
Weitere Informationen zum Erstellen von benutzerdefinierten Typen finden Sie hier.
Standardmäßig sind für Eigenschaften Getter und ein "Push"-Slot definiert, sowie ein Notify-Signal, das ausgegeben wird, wenn der Wert geändert wird. Qt Remote Objects benötigt das Notify-Signal auf dem Source-Objekt, um das Senden von Updates an die angeschlossenen Replicas auszulösen. In früheren Versionen von QtRO waren Eigenschaften standardmäßig lesbar/schreibbar, d.h. sie hatten Getter und Setter. Aufgrund der asynchronen Natur von QtRO führte dies jedoch manchmal zu unintuitivem Verhalten. Durch das Setzen des READWRITE-Attributs auf das PROP wird das alte Verhalten (Getter und Setter) wiederhergestellt.
// 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
Wenn es notwendig ist, zu blockieren, bis der Wert geändert wird, ist etwas wie das Folgende notwendig.
// 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 setzt nun standardmäßig READPUSH ein, was einen automatisch generierten Slot für die Anforderung einer Eigenschaftsänderung bereitstellt.
// 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)
Sie können auch die Schlüsselwörter CONSTANT
, READONLY
, PERSISTED
, READWRITE
, READPUSH
oder SOURCEONLYSETTER
in der PROP-Deklaration verwenden, was die Implementierung der Eigenschaft beeinflusst. READPUSH ist der Standardwert, wenn kein Wert verwendet wird.
PROP(int lifeUniverseEverything=42 CONSTANT) PROP(QString name READONLY)
Bitte beachten Sie, dass es hier einige Feinheiten gibt. Ein CONSTANT PROP hat eine Q_PROPERTY, die auf der SOURCE-Seite als CONSTANT deklariert ist. Die Replikate können den korrekten Wert jedoch erst kennen, wenn sie initialisiert werden, d. h. der Eigenschaftswert muss sich während der Initialisierung ändern dürfen. Für READONLY hat die Quelle weder einen Setter noch einen Push-Slot, und auf der Replikat-Seite wird kein Push-Slot erzeugt. Das Hinzufügen der PERSISTED-Eigenschaft zu einem PROP bewirkt, dass das PROP die QRemoteObjectAbstractPersistedStore -Instanz verwendet, die auf einem Knoten (falls vorhanden) eingestellt ist, um PROP-Werte zu speichern/wiederherzustellen.
Ein weiterer nuancierter Wert ist SOURCEONLYSETTER, der eine weitere Möglichkeit bietet, asymmetrisches Verhalten zu spezifizieren, wobei die Quelle (insbesondere die Hilfsklasse SimpleSource
) einen öffentlichen Getter und Setter für die Eigenschaft hat, diese aber auf der Replica-Seite ReadOnly (mit einem Notify-Signal) ist. Somit kann die Eigenschaft von der Quellseite aus vollständig kontrolliert, von der Replikaseite aus aber nur beobachtet werden. SOURCEONLYSETTER ist der Modus, der von repc für MODEL und CLASS Instanzen verwendet wird, d.h. die Quelle kann das Objekt, auf das gezeigt wird, ändern, aber das Replikat kann kein neues Objekt bereitstellen, da keine set<Prop> oder push<Prop> Methode erzeugt wird. Beachten Sie, dass dies keinen Einfluss auf das Verhalten der Eigenschaften des Typs hat, auf den gezeigt wird, sondern nur auf die Möglichkeit, den Zeiger selbst zu ändern.
CLASS
Das Schlüsselwort CLASS erzeugt spezielle Q_PROPERTY Elemente für von QObject abgeleitete Objekte. Diese Eigenschaften haben die gleiche Semantik wie SOURCEONLYSETTER. Die Syntax ist das Schlüsselwort CLASS
, gefolgt von dem Eigenschaftsnamen und dem Typ des Unterobjekts in Klammern.
// In .rep file class OtherClass { PROP(int value) } class MainClass { CLASS subObject(OtherClass) }
MODEL
Das Schlüsselwort MODEL erzeugt spezielle Q_PROPERTY Elemente für von QAbstractItemModel abgeleitete Objekte. Diese Eigenschaften haben die gleiche Semantik wie SOURCEONLYSETTER. Die Syntax besteht aus dem Schlüsselwort MODEL
, gefolgt vom Eigenschaftsnamen und den in Klammern eingeschlossenen (durch Komma getrennten) Rollen, die der Nachbildung zugänglich gemacht werden sollen.
// In .rep file class CdClass { PROP(QString title READONLY) MODEL tracks(title, artist, length) }
SIGNAL
Signalmethoden werden mit dem Schlüsselwort SIGNAL in der Replikationsdatei erstellt.
Die Verwendung besteht darin, SIGNAL
zu deklarieren, gefolgt von der gewünschten Signatur, die in Klammern gesetzt wird. Der void-Rückgabewert sollte übersprungen werden.
SIGNAL(test()) SIGNAL(test(QString foo, int bar)) SIGNAL(test(QMap<QString,int> foo)) SIGNAL(test(const QString &foo)) SIGNAL(test(QString &foo))
Genau wie in Qt queued connections werden Parameter in Signalen, die Referenzen sind, kopiert, wenn sie an Replikate übergeben werden.
SLOT
Slot-Methoden werden mit dem SLOT-Schlüsselwort in der rep-Datei erstellt.
Die Verwendung besteht darin, SLOT
zu deklarieren, gefolgt von der gewünschten Signatur, die in Klammern gesetzt wird. Der Rückgabewert kann in die Deklaration aufgenommen werden. Wenn der Rückgabewert weggelassen wird, wird in den generierten Dateien void verwendet.
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))
Genau wie in Qt queued connections und QtRO SIGNALS werden Parameter in Slots, die Referenzen sind, kopiert, wenn sie an Replicas übergeben werden.
ENUM
Aufzählungen (die eine Kombination aus C++ enum und Qt's Q_ENUM in QtRO verwenden) werden mit dem ENUM Schlüsselwort beschrieben.
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}
Verwandte Themen: Der ENUM-Typ, USE_ENUM-Schlüsselwort
Der POD-Typ
Plain Old Data (POD) ist ein Begriff zur Beschreibung einer einfachen Datensammlung, ähnlich wie eine C++-Struktur. Wenn Sie z. B. eine API für ein Telefonbuch haben, möchten Sie vielleicht das Konzept einer "Adresse" in der Schnittstelle verwenden (wobei die Adresse Straße, Stadt, Bundesland, Land und Postleitzahl enthalten kann). Sie können das Schlüsselwort POD verwenden, um solche Objekte zu definieren, die dann in PROP/SIGNAL/SLOT-Definitionen in Ihren Klassendefinitionen verwendet werden können.
Die Verwendung besteht darin, POD
zu deklarieren, gefolgt vom Namen des generierten Typs, gefolgt von Typ- und Namenspaaren, die durch Kommata getrennt sind, wobei die Typ/Namenspaare in Klammern eingeschlossen sind.
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)
Ein vollständiges Beispiel würde wie folgt aussehen
Der von repc generierte Code erstellt für jeden POD eine Klasse Q_GADGET mit entsprechenden Q_PROPERTY Mitgliedern für jeden für den POD definierten Typ.
Bei der Verwendung der generierten Header-Dateien innerhalb einer Bibliothek kann es erforderlich sein, Klassenattribute zu definieren, um die Sichtbarkeit von Symbolen festzulegen. Dies kann durch die Definition des Attributs nach dem Schlüsselwort POD
erfolgen. Im folgenden Beispiel wird das Makro MYSHAREDLIB_EXPORT
verwendet, das in "mysharedlib_global.h"
definiert ist. Weitere Informationen zur Funktionsweise finden Sie unter Gemeinsame Bibliotheken erstellen.
#include "mysharedlib_global.h" POD MYSHAREDLIB_EXPORT Foo(int bar)
Der ENUM-Typ
Oft ist es einfacher und sauberer, ein ENUM innerhalb einer Klasse zu definieren (siehe ENUM), aber wenn Sie einen eigenständigen Enum-Typ benötigen, kann die Verwendung des ENUM-Schlüsselworts außerhalb einer Klassendefinition hilfreich sein. Dadurch wird eine neue Klasse in Ihren Header-Dateien erzeugt, die das Marshalling usw. übernimmt. Die Syntax ist identisch mit ENUM, mit der Ausnahme, dass die Deklaration in diesem Fall nicht in einer class
Deklaration enthalten ist.
Verwandte Themen: ENUM, USE_ENUM-Schlüsselwort
USE_ENUM-Schlüsselwort
Das USE_ENUM-Schlüsselwort wurde implementiert, bevor die automatische Generierung über das ENUM-Schlüsselwort eingeführt wurde. Es wird aus Gründen der Abwärtskompatibilität beibehalten.
Verwandte Themen: ENUM, Der ENUM-Typ
Direktiven
Die rep-Datei definiert eine Schnittstelle, aber Schnittstellen erfordern oft externe Elemente. Um dies zu unterstützen, fügt repc beliebige (einzeilige) Direktiven an den Anfang der erzeugten Dateien. So können Sie zum Beispiel #include- oder #define-Direktiven verwenden, die die benötigte Logik oder Datentypen unterstützen.
Das repc-Tool ignoriert derzeit alles vom "#"-Symbol bis zum Zeilenende und fügt es den generierten Dateien hinzu. Mehrzeilige #if/#else/#endif-Anweisungen und mehrzeilige Makros werden also nicht unterstützt.
#HEADER- und #FOOTER-Anweisungen
Es gibt zwei spezielle Direktiven, #HEADER
und #FOOTER
. Diese Direktiven können verwendet werden, um Inhalte zu definieren, die im generierten Code entweder vor (HEADER) oder nach (FOOTER) der Schnittstellendeklaration eingefügt werden sollen. Die führenden Zeichen #HEADER
und #FOOTER
sowie ein Leerzeichen werden entfernt.
Im folgenden Beispiel werden die generierten repc-Klassen in einen Namespace eingefügt.
#HEADER namespace MyNamespace { class MyType { ... }; #FOOTER } // namespace MyNamespace
CMake-Funktionen
Die CMake-Funktionen zur Erzeugung von Quell- und Replikat-Typen sind im Folgenden aufgeführt.
Erzeugt C++-Header-Dateien für Quell- und Replikat-Typen aus den Qt Remote Objects.rep-Dateien. | |
Erzeugt C++-Header-Dateien für Replikat-Typen aus den Qt Remote Objects.rep-Dateien. | |
Erzeugt C++-Header-Dateien für Quelltypen aus den Qt Remote Objects.rep-Dateien. | |
Erzeugt .rep-Dateien aus den QObject-Header-Dateien. |
qmake-Variablen
REPC_REPLICA
Gibt die Namen aller Rep-Dateien im Projekt an, die zur Erzeugung von Replica-Header-Dateien verwendet werden sollen.
Zum Beispiel:
REPC_REPLICA = media.rep \ location.rep
Die erzeugte(n) Datei(en) haben die Form rep_<replica file base>_replica.h
.
REPC_SOURCE
Gibt die Namen aller Replikationsdateien im Projekt an, die zur Erzeugung von Quelltext-Header-Dateien verwendet werden sollen.
Zum Beispiel:
REPC_SOURCE = media.rep \ location.rep
Die generierte(n) Datei(en) haben die Form rep_<replica file base>_source.h
.
REPC_MERGED
Gibt die Namen aller Replikationsdateien im Projekt an, die verwendet werden sollen, um kombinierte (Quell- und Replikations-) Header-Dateien zu erzeugen.
Zum Beispiel:
REPC_MERGED = media.rep \ location.rep
Die generierte(n) Datei(en) haben die Form rep_<replica file base>_merged.h
.
Hinweis: Normalerweise befinden sich Quellen und Replikate in getrennten Prozessen oder Geräten, daher wird diese Variable normalerweise nicht verwendet.
QOBJECT_REP
Gibt die Namen bestehender QObject Header-Dateien an, die verwendet werden sollen, um entsprechende .rep-Dateien zu erzeugen.
Siehe auch QRemoteObjectAbstractPersistedStore.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.