1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
|
/****************************************************************************
**
** Copyright (C) 2017 Ford Motor Company
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\externalpage https://en.wikipedia.org/wiki/Domain-specific_language
\title Domain Specific Language (DSL)
*/
/*!
\page qtremoteobjects-repc.html
\title Qt Remote Objects Compiler
\brief The Qt Remote Objects Compiler creates \l {Source} and \l {Replica} header files
\ingroup overviews
\keyword repc
\section1 REPC Overview
The \underline {Rep}lica \underline {C}ompiler (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 \l {Source} and \l
{Replica} header files.
The Qt Remote Objects module also includes qmake variables (\l REPC_SOURCE, \l
REPC_REPLICA, and \l REPC_MERGED) that can be added to your project file to automatically run
repc, and add the resulting files to the list of files processed by
\l{moc}{Meta Object Compiler} during the build process, making use of Qt
Remote Objects in your projects simple.
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 \l {QRemoteObjectDynamicReplica} {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.
See the documentation \l {Source} {here} for information on using
the generated files in your code. Here we will focus on the repc format and
options.
\section1 The rep file format
The rep file format is a simple \l {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.
\section2 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.
To define a class use the \c class keyword, followed by the name you
want for your type, and then enclose your API in brackets like so
\code
class MyType
{
//PROP/SIGNAL/SLOT/ENUM declarations to define your API
};
\endcode
\section3 PROP
Q_PROPERTY elements are created by using the PROP keyword in the rep
file. The syntax is the \c PROP keyword followed by the definition enclosed
in parentheses, where the definition is the type, the name, and (optionally) a
default value or attributes.
\code
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)
\endcode
More information about creating custom types can be found \l {Creating
Custom Qt Types} {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.
\code
// 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
\endcode
If it is necessary to block until the value is changed, something like the
following is necessary.
\code
// 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.
\endcode
QtRO now defaults to READPUSH, which provides an automatically generated
slot for requesting a property change.
\code
// 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)
\endcode
You can also use the \c CONSTANT, \c READONLY, \c PERSISTED, \c READWRITE,
\c READPUSH, or \c SOURCEONLYSETTER keywords in the PROP declaration, which
affects how the property is implemented. READPUSH is the default value if no value
used.
\code
PROP(int lifeUniverseEverything=42 CONSTANT)
PROP(QString name READONLY)
\endcode
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 \l QRemoteObjectAbstractPersistedStore
instance set on a Node (if any) to save/restore PROP values.
Another nuanced value is SOURCEONLYSETTER, which provides another way of
specifying asymmetric behavior, where the \l Source (specifically the helper
class, \c SimpleSource) will have a public getter and setter for the
property, but it will be ReadOnly (with a notify signal) on the \l Replica
side. Thus the property can be fully controlled from the \l Source side,
but only observed from the \l Replica side. SOURCEONLYSETTER is the mode
used by repc for MODEL and CLASS instances, meaning the \l Source can
change the pointed to object, but the \l 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.
\sa QRemoteObjectAbstractPersistedStore
\section3 SIGNAL
\l [DOC QtCore] {Signals} {Signal} methods are created by using the SIGNAL keyword in the rep file.
Usage is to declare \c SIGNAL followed by the desired signature wrapped in
parentheses. The void return value should be skipped.
\code
SIGNAL(test())
SIGNAL(test(QString foo, int bar))
SIGNAL(test(QMap<QString,int> foo))
SIGNAL(test(const QString &foo))
SIGNAL(test(QString &foo))
\endcode
Just like in Qt \l {Qt::ConnectionType} {queued connections}, parameters in signals that are
references will be copied when being passed to replicas.
\section3 SLOT
\l [DOC QtCore] {Slots} {Slot} methods are created by using the SLOT keyword in the rep file.
Usage is to declare \c 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.
\code
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))
\endcode
Just like in Qt \l {Qt::ConnectionType} {queued connections} and QtRO
SIGNALS, parameters in slots that are references will be copied when being
passed to Replicas.
\section3 ENUM
Enumerations (which use a combination of C++ enum and Qt's Q_ENUM in QtRO)
are described using the ENUM keyword.
\code
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}
\endcode
Related topics: \l {The ENUM type}, \l {USE_ENUM keyword}
\section2 The POD type
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.
Usage is to declare \c 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.
\code
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)
\endcode
A full example would look like this
\code
POD Foo(QList<QString> bar)
class MyType
{
SIGNAL(sendCustom(Foo foo));
};
\endcode
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.
\section2 The ENUM type
It is often easier and cleaner to define an ENUM inside a class (see \l 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 \l
ENUM, with the exception that the declaration in this case is not contained
in a \c class declaration.
Related topics: \l {ENUM}, \l {USE_ENUM keyword}
\section2 USE_ENUM keyword
The USE_ENUM keyword was implemented before autogeneration via the ENUM
keyword was added. It is kept for backwards compatibility.
Related topics: \l {ENUM}, \l {The ENUM type}
\section2 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.
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.
\section1 qmake variables
\section2 REPC_REPLICA
Specifies the names of all rep files in the project that should be used to generate
replica header files.
For example:
\code
REPC_REPLICA = media.rep \
location.rep
\endcode
The generated file(s) will be of the form \c {rep_<replica file base>_replica.h}.
\section2 REPC_SOURCE
Specifies the names of all rep files in the project that should be used to generate
source header files.
For example:
\code
REPC_SOURCE = media.rep \
location.rep
\endcode
The generated file(s) will be of the form \c {rep_<replica file base>_source.h}.
\section2 REPC_MERGED
Specifies the names of all rep files in the project that should be used to generate
combined (source and replica) header files.
For example:
\code
REPC_MERGED = media.rep \
location.rep
The generated file(s) will be of the form \c {rep_<replica file base>_merged.h}.
\note Typically sources and replicas live in separate processes or devices, so this variable
is not commonly used.
\endcode
\section 2 QOBJECT_REP
Specifies the names of existing QObject header files that should be used to generate corresponding
.rep files.
*/
|