Qt Remote Objects 编译器
REPC 概述
Replica 编译器(repc) 会根据 API 定义文件生成QObject 头文件。 该文件(称为“rep”文件)使用特定的(文本)语法来描述 API。按照惯例,这些文件的扩展名为 .rep,是 Replica 的缩写。当这些文件由 repc 处理时,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)时源(Source)可用时,这会非常有用。
有关在代码中使用生成的文件的信息,请参阅此处的文档。本文将重点介绍 repc 格式及其选项。
rep 文件格式
rep 文件格式是一种简单的领域特定语言(DSL),用于描述Qt Remote Objects (QtRO)所支持的接口。由于 QtRO 是一个基于对象的系统,这些接口由通过对象提供的 API 定义,即具有属性、信号和槽的类。
注释
支持 C++ 风格的单行注释(// text )以及 C 风格的多行注释(/* text * / )。
类类型
在 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)有关创建自定义类型的更多信息,请参阅此处。
默认情况下,属性会定义获取器和“push”槽,并在值发生变化时发出 notify 信号。Qt Remote Objects 需要 Source 对象上的 notify 信号来触发向关联的 Replica 发送更新。 在 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 在源端(SOURCE)上会声明一个被定义为 CONSTANT 的Q_PROPERTY 。然而,副本在初始化完成之前无法得知正确的值,这意味着必须允许属性值在初始化过程中发生变化。 对于 READONLY,源端既没有 setter 也没有 push 槽,而副本端也不会生成 push 槽。向 PROP 添加 PERSISTED 特性后,该 PROP 将使用节点上设置的QRemoteObjectAbstractPersistedStore 实例(如有)来保存/恢复 PROP 值。
另一个细微的值是 SOURCEONLYSETTER,它提供了一种指定非对称行为的另一种方式:源端(具体而言是辅助类SimpleSource )将拥有该属性的公共 getter 和 setter,但在副本端该属性将设为只读(并带有 notify 信号)。 因此,该属性可以由源端完全控制,而副本端只能进行观察。 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
MODEL 关键字为从QAbstractItemModel 派生的对象生成特殊的Q_PROPERTY 元素。这些属性的语义与 SOURCEONLYSETTER 相同。其语法为:MODEL 关键字,后跟属性名,然后是括号中包含的应向副本暴露的(以逗号分隔的)角色。
// In .rep file
class CdClass
{
PROP(QString title READONLY)
MODEL tracks(title, artist, length)
}SIGNAL
信号方法是通过在 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 XML 中queued connections 一样,信号中作为引用传递的参数在传递给副本时会被复制。
SLOT
通过在 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 XMLqueued connections 和 QtRO SIGNALS 一样,插槽中作为引用传递的参数在传递给副本时会被复制。
ENUM
枚举(在 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++ struct 的简单数据集合。 例如,如果您有一个电话簿的 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 关键字会很有帮助。这将在您的头文件中生成一个新类,用于处理序列化等操作。 其语法与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 MyNamespaceCMake 函数
以下列出了用于生成源类型和副本类型的 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 头文件的名称。
© 2026 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.