Qt Remote Objects Compiler
Qt远程对象编译器
REPC Overview
REPC概述
The Replica Compiler (repc) generates QObject header files based on an API definition file. The file (called a "rep" file) uses a specific (text) syntax to describe the API. By convention, these files are given a .rep file extension, short for Replica. When these files are processed by repc, repc generates both Source and Replica header files.
副本编译器(repc)基于API定义文件生成QObject头文件。该文件(称为“rep”文件)使用特定的(文本)语法来描述API。按照惯例,这些文件的扩展名为.rep,是Replica的缩写。当这些文件由repc处理时,repc会生成源和副本头文件。
The Qt Remote Objects module also includes CMake functions and qmake variables that can be added to your project file to automatically run repc, and add the resulting files to the list of files processed by Meta Object Compiler during the build process, making use of Qt Remote Objects in your projects simple.
Qt远程对象模块还包括CMake函数和qmake变量,可以添加到项目文件中以自动运行repc,并将生成的文件添加到元对象编译器在构建过程中处理的文件列表中,从而在项目中简单地使用Qt远程对象。
While Qt Remote Objects supports sharing any QObject over the network (using enableRemoting on the Source side and acquireDynamic on the Replica side), there are a couple of advantages to letting repc define your objects. First of all, while DynamicReplicas are useful, they are more cumbersome to work with. The API is not known until the object is initialized, and using the API from C++ requires string lookups through QMetaObject's methods. Secondly, having the interface known at compile time finds any issues at compile vs. at runtime. Thirdly, the rep format supports default values, which can be handy if you are unable to ensure the Source is available when the Replica is instantiated.
虽然Qt远程对象支持通过网络共享任何QObject(在源端使用enableRemoting,在副本端使用acquireDynamic),但让repc定义对象有几个优点。首先,虽然DynamicReplicas很有用,但使用起来更麻烦。在对象初始化之前,API是未知的,并且使用C++中的API需要通过QMetaObject的方法进行字符串查找。其次,在编译时知道接口会发现编译时和运行时的任何问题。第三,rep格式支持默认值,如果在实例化副本时无法确保源可用,这可能会很方便。
See the documentation here for information on using the generated files in your code. Here we will focus on the repc format and options.
有关在代码中使用生成的文件的信息,请参阅此处的文档。在这里,我们将重点介绍repc格式和选项。
The rep file format
rep文件格式
The rep file format is a simple Domain Specific Language (DSL) for describing an interface supported over Qt Remote Objects (QtRO). Since QtRO is an object based system, these interfaces are defined by APIs available through objects, that is, classes with properties, signals, and slots.
rep文件格式是一种简单的领域特定语言(DSL),用于描述Qt远程对象(QtRO)支持的接口。由于QtRO是一个基于对象的系统,这些接口由通过对象可用的API定义,即具有属性、信号和槽的类。
The class type
类的类型
Each class defined in a rep file becomes a QObject in the generated header files, with the described API generated for you.
rep文件中定义的每个类都成为生成的头文件中的QObject,并生成所描述的API。
To define a class use the class
keyword, followed by the name you want for your type, and then enclose your API in brackets like so
要定义一个类,请使用class关键字,后跟类型所需的名称,然后将API括在括号中,如下所示
class MyType
{
//PROP/CLASS/MODEL/SIGNAL/SLOT/ENUM declarations to define your API
};
When using the generated header files inside a library, it might be needed to define class attributes to set the symbol visibility. This can be done similar to C++ by defining the attribute after the class
keyword. In the following example the MYSHAREDLIB_EXPORT
macro is used, which is defined in "mysharedlib_global.h"
. See Creating Shared Libraries for more information how this works.
在库中使用生成的头文件时,可能需要定义类属性来设置符号可见性。这可以通过在class关键字后定义属性来完成,类似于C++。在以下示例中,使用了MYSHAREDLIB_EXPORT宏,该宏在“mysharedlib_global
.h”中定义。有关其工作原理的更多信息,请参阅创建共享库。
#include "mysharedlib_global.h"
class MYSHAREDLIB_EXPORT MyType
{
...
};
PROP
Q_PROPERTY elements are created by using the PROP keyword in the rep file. The syntax is the PROP
keyword followed by the definition enclosed in parentheses, where the definition is the type, the name, and (optionally) a default value or attributes.
Q_PROPERTY元素是通过在rep文件中使用PROP关键字创建的。语法是PROP关键字,后面是括号中的定义,其中定义是类型、名称和(可选)默认值或属性。
PROP(bool simpleBool) // boolean named simpleBool
//名为simpleBool的布尔值
PROP(bool defaultFalseBool=false) // boolean named defaultFalseBool, with false
// as the default value
//名为defaultFalseBool的布尔值,带false作为默认值
PROP(int lifeUniverseEverything=42) // int value that defaults to 42
//默认为42的int值
PROP(QByteArray myBinaryInfo) // Qt types are fine, may need #include
// additional headers in your rep file
//Qt类型也可以,可能需要#include rep文件中的其他头文件
PROP(QString name CONSTANT) // Property with the CONSTANT attribute
//具有CONSTANT属性的属性
PROP(QString setable READWRITE) // Property with the READWRITE attribute
// note: Properties default to READPUSH
// (see description below)
//具有READWRITE属性的属性
//注意:属性默认为READPUSH
//(见下文说明)
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)
//自定义类型有效。需要#include 适合类型的头文件,
//确保元对象系统知道类型,并确保它支持队列连接(参见 Q_DECLARE_METATYPE和qRegisterMetaType)
More information about creating custom types can be found here.
关于创建自定义类型的更多信息,请点击此处。
By default, properties will have getters and a "push" slot defined, as well as a notify signal emitted when the value is changed. Qt Remote Objects requires the notify signal on the Source object to trigger sending updates to the attached Replicas. In earlier versions of QtRO, properties defaulted to being read/write, that is, having getters and setters. However, due to the asynchronous nature of QtRO, this led to unintuitive behavior at times. Setting the READWRITE attribute on the PROP will provide the old (getter and setter) behavior.
默认情况下,属性将定义getter和“push”槽,以及在值更改时发出通知信号。Qt远程对象需要源对象上的通知信号来触发向连接的副本发送更新。在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
If it is necessary to block until the value is changed, something like the following is necessary.
如果需要阻塞直到值更改,则需要以下内容。
// 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 now defaults to READPUSH, which provides an automatically generated slot for requesting a property change.
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)
You can also use the CONSTANT
, READONLY
, PERSISTED
, READWRITE
, READPUSH
, or SOURCEONLYSETTER
keywords in the PROP declaration, which affects how the property is implemented. READPUSH is the default value if no value used.
还可以在PROP声明中使用CONSTANT、READONLY、PERSISTED、READWRITE、READPUSH或SOURCEONLYSETTER关键字,这会影响属性的实现方式。如果未使用任何值,则READPUSH为默认值。
PROP(int lifeUniverseEverything=42 CONSTANT)
PROP(QString name READONLY)
Please note there are some subtleties here. A CONSTANT PROP has a Q_PROPERTY declared as CONSTANT on the SOURCE side. However, replicas cannot know the correct value until they are initialized, which means the property value has to be allowed to change during initialization. For READONLY, the source will have neither a setter nor a push slot, and the replica side will not have a push slot generated. Adding the PERSISTED trait to a PROP will have the PROP use the QRemoteObjectAbstractPersistedStore instance set on a Node (if any) to save/restore PROP values.
请注意这里有一些微妙之处。CONSTANT PROP在源端声明Q_PROPERTY为CONSTANT。但是,副本在初始化之前无法知道正确的值,这意味着必须允许在初始化过程中更改属性值。对于READONLY模式,源端既没有设置器也没有推送槽,副本端也不会生成推送槽。将PERSISTED trait添加到PROP将使PROP使用节点(如果有的话)上设置的QRemoteObjectAbstractPersistedStore实例来保存/恢复PROP值。
Another nuanced value is SOURCEONLYSETTER, which provides another way of specifying asymmetric behavior, where the Source (specifically the helper class, SimpleSource
) will have a public getter and setter for the property, but it will be ReadOnly (with a notify signal) on the Replica side. Thus the property can be fully controlled from the Source side, but only observed from the Replica side. SOURCEONLYSETTER is the mode used by repc for MODEL and CLASS instances, meaning the Source can change the pointed to object, but the Replica can not provide a new object, as no set<Prop> or push<Prop> method is generated. Note, this does not impact the behavior of the pointed to type's properties, just the ability to change the pointer itself.
另一个细微差别的值是SOURCEONLYSETTER,它提供了另一种指定不对称行为的方法,其中Source(特别是助手类SimpleSource)将为属性提供一个公共的getter和setter,但在副本端它将是只读的(带通知信号)。因此,该属性可以从源端完全控制,但只能从副本端观察。SOURCEONLYSETTER是repc用于MODEL和CLASS实例的模式,这意味着Source可以更改指向的对象,但Replica不能提供新对象,因为不会生成set<Prop>或push<Prop>方法。请注意,这不会影响指向类型属性的行为,只会影响更改指针本身的能力。
CLASS
The CLASS keyword generates special Q_PROPERTY elements for objects derived from QObject. These properties have the same semantics as SOURCEONLYSETTER. The syntax is the CLASS
keyword followed by the property name and then the type of subobject enclosed in parentheses.
CLASS关键字为从QObject派生的对象生成特殊的Q_PROPERTY元素。这些属性与SOURCEONLYSETTER具有相同的语义。语法是CLASS关键字,后跟属性名,然后是括号中包含的子对象类型。
// In .rep file
class OtherClass
{
PROP(int value)
}
class MainClass
{
CLASS subObject(OtherClass)
}
MODEL
The MODEL keyword generates special Q_PROPERTY elements for objects derived from QAbstractItemModel. These properties have the same semantics as SOURCEONLYSETTER. The syntax is the MODEL
keyword followed by the property name and then parentheses enclosing the (comma-separated) roles that should be exposed to the replica.
MODEL关键字为从QAbstractItemModel派生的对象生成特殊的Q_PROPERTY元素。这些属性与SOURCEONLYSETTER具有相同的语义。语法是MODEL关键字,后跟属性名,然后用括号括住应该向副本公开的(逗号分隔的)角色。
// In .rep file
class CdClass
{
PROP(QString title READONLY)
MODEL tracks(title, artist, length)
}
SIGNAL
Signal methods are created by using the SIGNAL keyword in the rep file.
信号方法是通过在rep文件中使用Signal关键字创建的。
Usage is to declare SIGNAL
followed by the desired signature wrapped in parentheses. The void return value should be skipped.
用法是声明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))
Just like in Qt queued connections, parameters in signals that are references will be copied when being passed to replicas.
就像在Qt排队连接中一样,作为引用的信号中的参数在传递给副本时将被复制。
SLOT
Slot methods are created by using the SLOT keyword in the rep file.
Slot方法是通过在rep文件中使用SLOT关键字创建的。
Usage is to declare SLOT
followed by the desired signature wrapped in parentheses. The return value can be included in the declaration. If the return value is skipped, void will be used in the generated files.
用法是声明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))
Just like in Qt queued connections and QtRO SIGNALS, parameters in slots that are references will be copied when being passed to Replicas.
就像在Qt队列连接和QtRO SIGNALS中一样,作为引用的插槽中的参数在传递给Replica时也会被复制。
ENUM
Enumerations (which use a combination of C++ enum and Qt's Q_ENUM in QtRO) are described using the ENUM keyword.
枚举(使用C++枚举和QtRO中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}
Related topics: The ENUM type, USE_ENUM keyword
相关主题:ENUM类型,USE_ENUM关键字
The POD type
POD类型
Plain Old Data (POD) is a term to describe a simple data collection, along the lines of a C++ struct. For example, if you have an API for a phonebook, you may want to use the concept of an "address" in its interface (where address might include street, city, state, country, and postal code). You can use the POD keyword to define objects like this, which can then be used by in PROP/SIGNAL/SLOT definitions in your class definitions.
普通旧数据(POD)是一个术语,用于描述简单的数据收集,类似于C++结构。例如,如果有电话簿的API,可能希望在其界面中使用“地址”的概念(其中地址可能包括街道、城市、州、国家和邮政编码)。可以使用POD关键字定义这样的对象,然后可以在类定义中的PROP/SIGNAL/SLOT定义中使用。
Usage is to declare POD
followed by the name for the generated type, followed by type and name pairs separated by commas, where the type/name pairs are wrapped in parentheses.
用法是声明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)
A full example would look like this
完整的示例如下
POD Foo(QList<QString> bar)
class MyType
{
SIGNAL(sendCustom(Foo foo));
};
The code generated by repc creates a Q_GADGET class for each POD, with corresponding Q_PROPERTY members for each type defined for the POD.
repc生成的代码为每个POD创建一个Q_GADGET类,为POD定义的每种类型都有相应的Q_PROPERTY成员。
When using the generated header files inside a library, it might be needed to define class attributes to set the symbol visibility. This can be done by defining the attribute after the POD
keyword. In the following example the MYSHAREDLIB_EXPORT
macro is used, which is defined in "mysharedlib_global.h"
. See Creating Shared Libraries for more information how this works.
在库中使用生成的头文件时,可能需要定义类属性来设置符号可见性。这可以通过在POD关键字后定义属性来实现。在以下示例中,使用了MYSHAREDLIB_EXPORT宏,该宏在“mysharedlib_global
.h”中定义。有关其工作原理的更多信息,请参阅创建共享库。
#include "mysharedlib_global.h"
POD MYSHAREDLIB_EXPORT Foo(int bar)
The ENUM type
ENUM类型
It is often easier and cleaner to define an ENUM inside a class (see ENUM), but if you need a standalone enum type, using the ENUM keyword outside of a class definition can be helpful. This will generate a new class in your header files that handles marshalling, etc.. The syntax is identical to ENUM, with the exception that the declaration in this case is not contained in a class
declaration.
在类中定义ENUM通常更容易、更清晰(请参阅ENUM),但如果需要一个独立的枚举类型,在类定义之外使用ENUM关键字可能会有所帮助。这将在头文件中生成一个处理编组等的新类。语法与ENUM相同,除了这种情况下的声明不包含在类声明中。
Related topics: ENUM, USE_ENUM keyword
相关主题:ENUM,USE_ENUM关键字
USE_ENUM keyword
USE_ENUM关键字
The USE_ENUM keyword was implemented before autogeneration via the ENUM keyword was added. It is kept for backwards compatibility.
USE_ENUM关键字是在添加通过ENUM关键字自动生成之前实现的。它是为了向后兼容性而保留的。
Related topics: ENUM, The ENUM type
相关主题:ENUM,ENUM类型
Directives
指令
The rep file defines an interface, but interfaces often require external elements. In order to support this, repc will include any (single line) directives at the top of the generated files. This allows you to, for instance, use #include or #define directives that support the logic or datatypes needed.
rep文件定义了一个接口,但接口通常需要外部元素。为了支持这一点,repc将在生成的文件顶部包含任何(单行)指令。例如,这允许使用#include或#define指令来支持所需的逻辑或数据类型。
The repc tool currently ignores everything from the "#" symbol to the end-of-line and adds that to the generated files. So multi-line #if/#else/#endif statements and multi-line macros are not supported.
repc工具目前忽略从“#”符号到行尾的所有内容,并将其添加到生成的文件中。因此,不支持多行#if/#else/#endif语句和多行宏。
#HEADER and #FOOTER Directives
#HEADER和#FOOTER指令
There are two special directives, #HEADER
and #FOOTER
. Those directives can be used to define content which should be put as-is into the generated code, either before (HEADER) or after (FOOTER) the interfaces declaration. The leading #HEADER
and #FOOTER
tokens plus one white-space character are stripped.
有两个特殊指令,#HEADER和#FOOTER。这些指令可用于定义应按原样放入生成的代码中的内容,可以在接口声明之前(HEADER)或之后(FOOTER)。前导#HEADER和#FOOTER标记加上一个空格字符被删除。
In the following example, the generated repc classes are put inside a namespace.
在下面的示例中,生成的repc类被放入命名空间中。
#HEADER namespace MyNamespace {
class MyType
{
...
};
#FOOTER } // namespace MyNamespace
CMake functions
CMake函数
The CMake functions for generating source and replica types are listed below.
下面列出了用于生成源和副本类型的CMake函数。
Creates C++ header files for source and replica types from the Qt Remote Objects .rep files. 从Qt远程对象.rep文件为源和副本类型创建C++头文件。 | |
Creates C++ header files for replica types from the Qt Remote Objects .rep files. 从Qt远程对象.rep文件为副本类型创建C++头文件。 | |
Creates C++ header files for source types from the Qt Remote Objects .rep files. 从Qt远程对象.rep文件为源代码类型创建C++头文件。 | |
Creates .rep files from the QObject header files. 从QObject头文件创建.rep文件。 |
qmake variables
qmakes变量
REPC_REPLICA
Specifies the names of all rep files in the project that should be used to generate replica header files.
指定项目中应用于生成副本头文件的所有rep文件的名称。
For example:
例如:
REPC_REPLICA = media.rep \
location.rep
The generated file(s) will be of the form rep_<replica file base>_replica.h
.
生成的文件格式为rep_<replica file base>_replica.h。
REPC_SOURCE
Specifies the names of all rep files in the project that should be used to generate source header files.
指定项目中应用于生成源头文件的所有rep文件的名称。
For example:
例如:
REPC_SOURCE = media.rep \
location.rep
The generated file(s) will be of the form rep_<replica file base>_source.h
.
生成的文件格式为rep_<replica file base>_source.h。
REPC_MERGED
Specifies the names of all rep files in the project that should be used to generate combined (source and replica) header files.
指定项目中应用于生成组合(源和副本)头文件的所有rep文件的名称。
For example:
例如:
REPC_MERGED = media.rep \
location.rep
The generated file(s) will be of the form rep_<replica file base>_merged.h
.
生成的文件格式为rep_<replica file base>_merged.h。
Note: Typically sources and replicas live in separate processes or devices, so this variable is not commonly used.
注意:通常源和副本位于单独的进程或设备中,因此此变量不常用。
QOBJECT_REP
Specifies the names of existing QObject header files that should be used to generate corresponding .rep files.
指定应用于生成相应.rep文件的现有QObject头文件的名称。
See also QRemoteObjectAbstractPersistedStore.
另请参见QRemoteObjectAbstractPersistedStore。