Qt Remote Objects 编译器
REPC 概述
Replica 编译器(repc) 可根据 API 定义文件生成QObject 头文件。该文件(称为 "rep "文件)使用特定的(文本)语法来描述 API。按照惯例,这些文件的扩展名为 .rep,是 Replica 的缩写。repc 处理这些文件时,会生成源文件和Replica头文件。
Qt Remote Objects 模块还包括CMake 函数和qmake 变量,可添加到项目文件中自动运行 repc,并将生成的文件添加到Meta-Object Compiler在构建过程中处理的文件列表中,从而使在项目中使用Qt Remote Objects 变得简单。
虽然Qt Remote Objects 支持通过网络共享任何QObject (在源端使用 enableRemoting,在副本端使用 acquireDynamic),但让 repc 来定义对象有几个优势。首先,虽然DynamicReplicas 很有用,但使用起来比较麻烦。在对象初始化之前,API 是未知的,而使用 C++ 的 API 需要通过QMetaObject 的方法查找字符串。其次,在编译时就知道接口,可以发现编译时与运行时的问题。第三,rep 格式支持默认值,如果无法确保在实例化 Replica 时源代码可用,这将非常方便。
有关在代码中使用生成文件的信息,请参阅此处的文档。在此,我们将重点介绍 repc 格式和选项。
rep 文件格式
rep 文件格式是一种简单的特定领域语言(DSL),用于描述Qt Remote Objects (QtRO) 支持的接口。由于 QtRO 是一个基于对象的系统,这些接口是通过对象(即具有属性、信号和插槽的类)提供的 API 定义的。
类类型
rep 文件中定义的每个类都会在生成的头文件中成为QObject ,并为您生成所描述的 API。
要定义一个类,请使用class
关键字,后面跟上你想要的类型名称,然后用括号括住你的 API,如下所示
class MyType { //PROP/CLASS/MODEL/SIGNAL/SLOT/ENUM declarations to define your API };
在库中使用生成的头文件时,可能需要定义类属性来设置符号可见性。这可以通过在class
关键字后定义属性来实现,与 C++ 类似。在下面的示例中,使用了MYSHAREDLIB_EXPORT
宏,该宏定义在"mysharedlib_global.h"
中。有关其工作原理的更多信息,请参阅创建共享库。
#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)
有关创建自定义类型的更多信息,请点击此处。
默认情况下,属性会定义获取器和 "推送 "槽,以及在值发生变化时发出的通知信号。Qt Remote Objects 需要源对象上的通知信号来触发向附加的副本发送更新。在 QtRO 的早期版本中,属性默认为读/写属性,即具有获取器和设置器。然而,由于 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 在 SOURCE 端有一个声明为 CONSTANT 的Q_PROPERTY 。但是,副本在初始化之前无法知道正确的值,这意味着必须允许属性值在初始化过程中发生变化。对于 READONLY,源代码既没有设置器,也没有推送槽,复制端也不会生成推送槽。为 PROP 添加 PERSISTED 特性后,PROP 将使用节点上设置的QRemoteObjectAbstractPersistedStore 实例(如果有的话)来保存/恢复 PROP 值。
另一个细微差别值是 SOURCEONLYSETTER,它提供了另一种指定非对称行为的方法,其中源(特别是辅助类SimpleSource
)将为属性提供一个公共 getter 和 setter,但在复制端它将是只读的(带有通知信号)。因此,源端可以完全控制该属性,但复制端只能观察到该属性。SOURCEONLYSETTER 是 repc 用于 MODEL 和 CLASS 实例的模式,这意味着源可以更改指向的对象,但复制不能提供新对象,因为不会生成 set<Prop> 或 push<Prop> 方法。请注意,这不会影响指向类型属性的行为,只是影响改变指针本身的能力。
CLASS
CLASS 关键字为从QObject 派生的对象生成特殊的Q_PROPERTY 元素。这些属性的语义与 SOURCEONLYSETTER 相同。语法是CLASS
关键字,后面是属性名称,然后是用括号括起来的子对象类型。
// In .rep file class OtherClass { PROP(int value) } class MainClass { CLASS subObject(OtherClass) }
模式
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))
就像在 Qt XMLqueued 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 中一样,当传递给 Replicas 时,槽中作为引用的参数将被复制。
枚举
枚举(在 QtRO 中结合使用 C++ enum 和 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}
相关主题:ENUM 类型、USE_ENUM 关键字
POD 类型
Plain Old Data(POD)是一个描述简单数据集合的术语,与 C++ 结构类似。例如,如果您有一个电话簿 API,您可能希望在其界面中使用 "地址 "的概念(地址可能包括街道、城市、州、国家和邮政编码)。您可以使用 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)
完整的示例如下
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),但如果需要独立的枚举类型,在类定义之外使用 ENUM 关键字会很有帮助。这将在头文件中生成一个新类,用于处理 marshalling 等。其语法与ENUM 相同,不同之处在于这种情况下的声明不包含在class
声明中。
相关主题:ENUM、USE_ENUM 关键字
USE_ENUM 关键字
USE_ENUM 关键字是在通过 ENUM 关键字自动生成之前实现的。保留该关键字是为了向后兼容。
指令
rep 文件定义了一个接口,但接口通常需要外部元素。为了支持这一点,repc 会在生成文件的顶部包含任何(单行)指令。这样,您就可以使用支持所需逻辑或数据类型的 #include 或 #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 Remote Objects.rep 文件创建源类型和副本类型的 C++ 头文件。 | |
从Qt Remote Objects.rep 文件为副本类型创建 C++ 头文件。 | |
从Qt Remote Objects.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 头文件的名称。
© 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.