Spec-Zone.ru › Qt 5.15

Компилятор Qt удалённых объектов

Обзор REPC

Компилятор Реплики (repc) генерирует заголовочные файлы QObject на основе файла определения API. Файл (называемый "rep-файлом") использует специфический (текстовый) синтаксис для описания API. По соглашению, этим файлам присваивается расширение .rep, сокращение от Replica. При обработке этих файлов repc генерирует заголовочные файлы как для источника, так и для реплики.

Модуль Qt удалённых объектов также включает переменные qmake (REPC_SOURCE, REPC_REPLICA и REPC_MERGED), которые можно добавить в ваш проект, чтобы автоматически запускать repc и добавлять полученные файлы в список файлов, обрабатываемых Компилятором метаобъектов во время сборки. Это упрощает использование Qt удалённых объектов в ваших проектах.

Хотя модуль Qt удалённых объектов поддерживает обмен любым объектом QObject по сети (используя enableRemoting со стороны источника и acquireDynamic со стороны реплики), существуют некоторые преимущества использования repc для определения ваших объектов. Во-первых, хотя DynamicReplicas полезны, с ними сложнее работать. API неизвестен до инициализации объекта, а использование API из C++ требует поиска строк через методы QMetaObject. Во-вторых, знание интерфейса на этапе компиляции позволяет обнаруживать проблемы на этапе компиляции, а не во время выполнения. В-третьих, формат rep поддерживает значения по умолчанию, что может быть полезно, если вы не можете гарантировать доступность источника при создании реплики.

См. документацию здесь для получения информации о использовании сгенерированных файлов в вашем коде. Здесь мы сосредоточимся на формате и параметрах repc.

Формат rep-файла

Формат rep-файла представляет собой простой язык предметной области (DSL) для описания интерфейса, поддерживаемого Qt удалёнными объектами (QtRO). Поскольку QtRO — это объектно-ориентированная система, эти интерфейсы определяются доступными через объекты API, то есть классами с свойствами, сигналами и слотами.

Тип класса

Каждый класс, определённый в rep-файле, становится объектом QObject в сгенерированных заголовочных файлах с сгенерированным для вас описанным API.

Для определения класса используется ключевое слово class, за которым следует имя желаемого типа, а затем API заключается в скобки, как показано ниже

class MyType
{
    //PROP/CLASS/MODEL/SIGNAL/SLOT/ENUM declarations to define your API
};

СВОЙСТВО

Элементы Q_PROPERTY создаются с помощью ключевого слова PROP в rep-файле. Синтаксис — ключевое слово 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 удалённые объекты требуют сигнала notify в объекте источника для запуска отправки обновлений присоединённым репликам. В более ранних версиях QtRO свойства по умолчанию были читаемыми/записываемыми, то есть имели геттеры и сеттеры. Однако из-за асинхронного характера QtRO это иногда приводило к неинтуитивному поведению. Установка атрибута READWRITE в PROP обеспечит старое поведение (геттер и сеттер).

// 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)

Вы также можете использовать ключевые слова CONSTANT, READONLY, PERSISTED, READWRITE, READPUSH, или SOURCEONLYSETTER в объявлении PROP, что повлияет на реализацию свойства. READPUSH — значение по умолчанию, если не указано иное.

PROP(int lifeUniverseEverything=42 CONSTANT)
PROP(QString name READONLY)

Обратите внимание на некоторые нюансы. Свойство CONSTANT PROP имеет Q_PROPERTY, объявленное как CONSTANT со стороны источника. Однако реплики не могут узнать правильное значение до инициализации, что означает, что значению свойства нужно разрешить изменение во время инициализации. Для READONLY источник не будет иметь ни сеттера, ни слота push, а реплика не будет иметь сгенерированного слота push. Добавление атрибута PERSISTED к PROP приведет к использованию экземпляра QRemoteObjectAbstractPersistedStore, установленного в узле (если таковой имеется), для сохранения/восстановления значений PROP.

Ещё один тонкий момент — SOURCEONLYSETTER, который предлагает другой способ определения асимметричного поведения, где источник (точнее, вспомогательный класс SimpleSource) будет иметь общедоступный геттер и сеттер для свойства, но оно будет ReadOnly (с сигналом notify) со стороны реплики. Таким образом, свойство полностью контролируется со стороны источника, но только наблюдается со стороны реплики. SOURCEONLYSETTER — это режим, используемый repc для экземпляров MODEL и CLASS, что означает, что источник может изменить указываемый объект, но реплика не может предоставить новый объект, так как не генерируется метод set<Prop> или push<Prop>. Обратите внимание, это не влияет на поведение свойств указываемого типа, а только на возможность изменения указателя.

КЛАСС

Ключевое слово CLASS генерирует специальные элементы Q_PROPERTY для объектов, производных от QObject. Эти свойства имеют те же семантики, что и SOURCEONLYSETTER. Синтаксис — ключевое слово CLASS за которым следует имя свойства, а затем тип подобъекта в скобках.

// In .rep file
class OtherClass
{
    PROP(int value)
}

class MainClass
{
    CLASS subObject(OtherClass)
}

МОДЕЛЬ

Ключевое слово MODEL генерирует специальные элементы Q_PROPERTY для объектов, производных от QAbstractItemModel. Эти свойства имеют те же семантики, что и SOURCEONLYSETTER. Синтаксис — ключевое слово MODEL за которым следует имя свойства, а затем скобки с (разделенными запятыми) ролями, которые должны быть доступны для реплики.

// In .rep file
class CdClass
{
    PROP(QString title READONLY)
    MODEL tracks(title, artist, length)
}

СИГНАЛ

Методы сигналов создаются с помощью ключевого слова SIGNAL в rep-файле.

Использование заключается в объявлении 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 очередных подключениях, параметры сигналов, являющиеся ссылками, копируются при передаче репликам.

СЛОТ

Методы слотов создаются с помощью ключевого слова SLOT в rep-файле.

Использование заключается в объявлении 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, параметры слотов, являющиеся ссылками, копируются при передаче репликам.

ПЕРЕЧИСЛЕНИЕ

Перечисления (которые используют сочетание C++ enum и Qt Q_ENUM в QtRO) описываются с помощью ключевого слова 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

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)

Полный пример выглядит так

POD Foo(QList<QString> bar)
class MyType
{
    SIGNAL(sendCustom(Foo foo));
};

Код, сгенерированный repc, создаёт класс Q_GADGET для каждого POD с соответствующими членами Q_PROPERTY для каждого определённого типа POD.

Тип ПЕРЕЧИСЛЕНИЯ

Часто проще и чище определять ПЕРЕЧИСЛЕНИЕ внутри класса (см. ENUM), но если вам нужен отдельный тип перечисления, использование ключевого слова ENUM вне определения класса может быть полезным. Это сгенерирует новый класс в ваших заголовочных файлах, который обрабатывает маршаллирование и т. д.. Синтаксис идентичен ENUM за исключением того, что в этом случае объявление не заключено в объявление class.

Связанные темы: ENUM, ключевое слово USE_ENUM

Ключевое слово USE_ENUM

Ключевое слово USE_ENUM было реализовано до добавления автоматической генерации через ключевое слово ENUM. Оно сохранено для обратной совместимости.

Связанные темы: ENUM, Тип ПЕРЕЧИСЛЕНИЯ

Директивы

rep-файл определяет интерфейс, но интерфейсы часто требуют внешних элементов. Для поддержки этого repc включит все (однострочные) директивы в начале сгенерированных файлов. Это позволяет, например, использовать директивы #include или #define, которые поддерживают необходимую логику или типы данных.

Инструмент repc в настоящее время игнорирует всё от символа "#" до конца строки и добавляет это в сгенерированные файлы. Таким образом, многострочные #if/#else/#endif и многострочные макросы не поддерживаются.

Переменные 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

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.

QOBJECT_REP

Указывает имена существующих заголовочных файлов QObject, которые должны использоваться для генерации соответствующих файлов .rep.

См. также QRemoteObjectAbstractPersistedStore.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.15/qtremoteobjects-repc.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API