Qt远程对象编译器
REPC概述
Replica (Rep) 编译器(repc)根据API定义文件生成QObject头文件。该文件(称为“rep”文件)使用特定的(文本)语法来描述API。按照惯例,这些文件以 reps 为扩展名,即复制品。当这些文件由repc处理时,repc会同时生成 Source 和 Replica 头文件。
Qt远程对象模块还包括CMake函数和qmake变量,可以将它们添加到项目文件中以自动运行repc,并在构建过程中将输出文件添加到MOC(元对象编译器)处理的文件列表中,使在项目中使用Qt远程对象变得简单。
虽然Qt远程对象支持通过网络(在源端使用enableRemoting,在副本端使用acquireDynamic)共享任何QObject,但让repc定义你的对象有几个优点。首先,虽然DynamicReplicas很有用,但使用起来更为复杂。直到对象初始化后才知道API,并且从C++中使用API需要通过QMetaObject的字符串查找方法。其次,在编译时知道接口可以在编译时与运行时查找问题。第三,rep格式支持默认值,如果无法确保在副本实例化时源可用,这可能会很有用。
有关在代码中使用生成文件的信息,请参阅此处的文档。在此我们将重点介绍repc格式和选项。
rep文件格式
rep文件格式是一种用于描述通过Qt远程对象(QtRO)支持的接口的简单领域特定语言(DSL)。由于QtRO是一个面向对象的系统,因此这些接口由通过对象提供的API定义,即具有属性、信号和槽的类。
类类型
在rep文件中定义的每个类都将成为生成头文件中的QObject,并为您生成描述的API。
要定义类,请使用class
关键字,后跟您想要的类型名称,然后将您的API括在大括号内,如下
class MyType { //PROP/CLASS/MODEL/SIGNAL/SLOT/ENUM declarations to define your API };
在库内部使用生成的头文件时,可能需要定义类属性来设置符号可见性。这可以通过类似于C++的方式来实现,即在class
关键字之后定义属性。以下示例中使用了MYSHAREDLIB_EXPORT
宏,该宏定义在"mysharedlib_global.h"
中。有关更多信息,请参阅创建共享库。
#include "mysharedlib_global.h" class MYSHAREDLIB_EXPORT MyType { ... };
PROP
使用rep文件中的PROP
关键字创建Q_PROPERTY
元素。语法为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)
有关创建自定义类型的更多信息,请参阅这里。
默认情况下,属性将定义getter和一个"push"插槽,以及在值更改时发出notify信号。Qt远程对象需要源对象的notify信号来触发发送更新到附加的Replicas。在QtRO的早期版本中,属性默认为读写,即具有getter和setter。然而,由于QtRO的异步特性,这有时会导致不直观的行为。将在PROP上设置READWRITE属性以提供旧的(getter和setter)行为。
// 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,源将不会有setter和push插槽,副本端也不会生成push插槽。将PERSISTED特性添加到PROP会使PROP使用在节点(如果有)上设置的QRemoteObjectAbstractPersistedStore
实例来保存/恢复PROP值。
另一个细微的值是SOURCEONLYSETTER,它提供了另一种指定非对称行为的方式,其中源(特别是辅助类SimpleSource
)将具有属性的公共getter和setter,但在副本端将是ReadOnly(带有notify信号)。因此,属性可以从源端完全控制,但从副本端只能观察。SOURCEONLYSETTER是repc用于MODEL和CLASS实例的模式,这意味着源可以更改指向的对象,但副本不能提供新的对象,因为没有生成set
CLASS
CLASS
关键字为继承自QObject的对象生成特殊的Q_PROPERTY
元素。这些属性具有与SOURCEONLYSETTER相同的语义。语法是CLASS
关键字后跟属性名称,然后是圆括号中包含的子对象类型。
// In .rep file class OtherClass { PROP(int value) } class MainClass { CLASS subObject(OtherClass) }
模型
使用“模型”关键字为从QAbstractItemModel派生的对象生成特殊的Q_PROPERTY元素。这些属性与SOURCEONLYSETTER具有相同的语义。语法是紧跟在属性名称后面的模型
关键字,然后是包含要公开给副本的角色(以逗号分隔)的括号。
// In .rep file class CdClass { PROP(QString title READONLY) MODEL tracks(title, artist, length) }
信号
在rep文件中使用信号关键字创建信号方法。
使用方法是声明紧跟在括号中的信号签名,其中所期望的签名在括号中被连接,void返回值应忽略。
SIGNAL(test()) SIGNAL(test(QString foo, int bar)) SIGNAL(test(QMap<QString,int> foo)) SIGNAL(test(const QString &foo)) SIGNAL(test(QString &foo))
就像Qt中的队列连接一样,传递到副本的信号引用参数将进行复制。
槽
在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))
就像在Qt中的队列连接和QtRO SIGNALS一样,传递到副本的槽引用参数将进行复制。
枚举
在QtRO中使用C++枚举和Qt的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}
相关主题:枚举类型,USE_ENUM关键字
POD类型
原始老数据(POD)是一个描述简单数据集合的术语,类似于C++的结构。例如,如果你有一个电话簿的API,你可能会在接口中使用“地址”的概念(其中地址可能包括街道、城市、州、国家以及邮政编码)。你可以使用POD关键字来定义这样的对象,然后在类定义中的PROP/SIGNAL/SLOT定义中使用。
使用方法是声明紧跟在括号中的类型名称,后面是类型/名称对,用逗号分隔,类型/名称对被括号包围。
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)
一个完整的例子看起来像这样
REPC生成的代码为每个POD创建一个Q_GADGET类,并为POD定义的每个类型都创建相应的Q_PROPERTY成员。
当在库内部使用生成的头文件时,可能需要定义类属性以设置符号可见性。这可以通过在POD
关键字后面定义属性来实现。在以下示例中使用了MYSHAREDLIB_EXPORT
宏,该宏在"mysharedlib_global.h"
中定义。有关更详细的信息,请参阅创建共享库。
#include "mysharedlib_global.h" POD MYSHAREDLIB_EXPORT Foo(int bar)
枚举类型
在类中定义枚举通常更简单、更干净(请参阅ENUM),但如果你需要一个独立的枚举类型,在类定义之外使用ENUM关键字可能有助于。这将创建一个处理编组等的新类。语法与ENUM相同,但声明不包含在class
声明中。
相关主题:ENUM,USE_ENUM关键字
USE_ENUM关键字
USE_ENUM关键字是在通过ENUM关键字添加自动生成之前实现的。它被保留以支持向后兼容。
指令
rep文件定义了一个接口,但接口通常需要外部元素。为了支持这一点,repc将在生成的文件顶部包含任何(单行)指令。这允许您使用包括或#define指令,这些指令支持所需的逻辑或数据类型。
repc工具现在忽略从"#"符号到行尾的所有内容,并将这些内容添加到生成的文件中。因此,多行#if/#else/#endif语句和多行宏不受支持。
#HEADER和#FOOTER指令
有两个特殊指令,分别是#HEADER
和#FOOTER
。这些指令可以用来定义应原样放入生成的代码中的内容,即接口声明之前(HEADER)或之后(FOOTER)。删除了#HEADER
和#FOOTER
的前导标记以及一个空白字符。
在以下示例中,生成的repc类被放入一个命名空间中。
#HEADER namespace MyNamespace { class MyType { ... }; #FOOTER } // namespace MyNamespace
CMake函数
以下列出了用于生成源和副本类型的CMake函数。
从Qt远程对象.rep文件创建源和副本类型的C++头文件。 | |
从Qt远程对象.rep文件创建副本类型的C++头文件。 | |
从Qt远程对象.rep文件创建源类型的C++头文件。 | |
从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头文件名称。
© 2024 Qt公司有限公司。此处包含的文档贡献为各自所有者的版权。提供的文档是根据自由软件基金会发布的GNU自由文档许可证版本1.3许可的。Qt及其相关标志是芬兰和/或世界其他地区的The Qt Company Ltd.的商标。所有其他商标均为其各自所有者的财产。