二 Qt Remote Objects (REPC 编译器)

2024-06-20 07:38
文章标签 qt 编译器 remote objects repc

本文主要是介绍二 Qt Remote Objects (REPC 编译器),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

REPC 概述

Replica Compiler ( repc )基于 API 定义文件生成QObject头文件。该文件(称为“rep”文件)使用特定的(文本)语法来描述 API。文件扩展名为 .rep,是 Replica 的缩写。当这些文件被repc 处理时,repc 会同时生成Source和Replica头文件。

Qt Remote Objects 模块还包括 qmake 宏(REPC_SOURCE,REPC_REPLICA),可以将它们添加到您的项目文件中以自动运行 repc。

IPC本质上就是调用远程进程的功能。怎么定义能够在QtRO中共享的功能类呢?

QtRO中的功能类没什么特殊的,其实就是个QObject派生类,所以任何派生自QObject的类型都能够分享到QtRO网络中。但是为了更好地和其他模块开发者协同,我推荐使用rep文件定义接口然后再实现的方式来编写功能类。

rep文件是一种DSL(Domain Specific Language),专门用于定义QtRO接口。在编译的时候,该文件会首先经过repc.exe这个程序处理,生成对应的头文件和源文件。只要安装Qt时选择了Qt RemoteObjects模块,repc.exe就在Qt安装目录的bin目录中。

使用Recp来定义接口有以下优点(对比Dyanmic Replica):
1 使用Dynamic Replica,API 在对象初始化之前是未知的,并且使用 C++ 中的 API 需要通过QMetaObject的方法进行字符串查找;
2 在编译时知道接口的任何问题;
3 rep 格式支持默认值,如果在实例化 Replica 时无法确保 Source 可用,这会很方便。

repc 文件格式

rep 文件格式是一种简单的域特定语言 (DSL),用于描述 Qt 远程对象 (QtRO) 上支持的接口。由于 QtRO 是一个基于对象的系统,这些接口由通过对象可用的 API 定义,即具有属性、信号和插槽的类。

class 类型

在 rep 文件中定义的每个类都成为生成的头文件中的QObject,并为您生成所描述的 API。

要定义一个类,请使用class关键字,后跟您想要的类型名称,然后将您的 API 括在括号中,如下所示

class MyType { //PROP/SIGNAL/SLOT/ENUM 声明来定义你的 API }; 

PROP

Q_PROPERTY元素是使用 rep 文件中的 PROP 关键字创建的。语法是PROP关键字后跟用引号括起来的定义,其中定义是类型、名称和(可选)默认值或属性。

PROP(bool simpleBool)                 // 名为 simpleBool 的布尔值
PROP(bool defaultFalseBool = false )     // 名为 defaultFalseBool 的布尔值,默认值为falsePROP( int lifeUniverseEverything = 42 )   // 默认为 42 的 int 值
PROP( QByteArray myBinaryInfo)         // Qt 类型很好,可能需要 #include // 在您的 rep 文件中添加额外的头文件PROP( QString name CONSTANT)           // 带有 CONSTANT 属性的属性
PROP( QString setable READWRITE)       // 带有 READWRITE 属性的属性// 注意:属性默认为 READPUSH // (参见下面的描述)PROP(SomeOtherType myCustomType)      // 自定义类型有效。需要#include 用于// 您的类型的 适当的标头,确保// 确保元对象// 系统知道您的类型,并确保它支持排队// 连接(参见Q_DECLARE_METATYPE 和// qRegisterMetaType)

默认情况下,属性将定义 getter 和“推送”插槽,以及在值更改时发出的通知信号。Qt Remote Objects 需要 Source 对象上的 notify 信号来触发向附加副本发送更新。在 QtRO 的早期版本中,属性默认为读/写,即具有 getter 和 setter。然而,由于 QtRO 的异步特性,这有时会导致不直观的行为。在 PROP 上设置 READWRITE 属性将提供旧的(getter 和 setter)行为。

// 在 .rep 文件中,旧的(setter)行为
PROP ( int myVal READWRITE ) // 使用 setMyVal(int myVal) 方法的旧行为             
// 在代码中...假设 myVal 在 Source 中初始设置为 0 
int originalValue = rep - > myVal (); // 将是 0 
rep - > setMyVal ( 10 ); // 调用 setter,期待阻塞// 非异步返回
如果( rep- > myVal ( ) == 10 ). . // 测试通常会失败     

如果需要在值更改之前进行阻塞,则需要执行以下操作。

// 在 .rep 文件中,旧的(setter)行为
PROP ( int myVal READWRITE ) // 使用 setMyVal(int myVal) 方法的旧行为             // 在代码中...假设 myVal 在 Source 中初始设置为 0 
bool originalValue = rep - > myVal (); // 将为 0// 我们可以使用 QSignalSpy 等待变化
QSignalSpy spy ( rep , SIGNAL ( myValChanged ( int )));
rep- > setMyVal (10);// 调用 setter,期待阻塞// 非异步返回                    
spy.wait()      //阻塞,直到收到更改的信号if ( rep - > myVal () == 10 ). . // 假设测试将成功 // 1. 源对象已连接 //2. 没有其他人(源或其他副本) 将 myVal 设置为其他东西(竞争条件)而不是使用 QSignalSpy,//事件驱动的做法是将 myValChanged 通知信号连接到响应更改的 Slot。  

QtRO 现在默认为 READPUSH,它提供了一个自动生成的 Slot 用于请求属性更改。

// In .rep file, defaults to READPUSHPROP(bool myVal)                      // No setMyVal(int myVal) on Replica, has// pushMyVal(int myVal) instead// In code...  Assume myVal is initially set to 0 in Sourcebool originalValue = rep->myVal();    // Will be 0// We can wait for the change using \l QSignalSpyQSignalSpy 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 receivedif (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关键字,这会影响其实现方式。如果未使用任何值,则 READPUSH 是默认值。

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

请注意这里有一些微妙之处。CONSTANT PROP 在 SOURCE 端有一个Q_PROPERTY声明为 CONSTANT。但是,副本在初始化之前无法知道正确的值,这意味着必须允许在初始化期间更改属性值。对于 READONLY,Source 既没有设置器也没有推送槽,副本端不会生成推送槽。将 PERSISTED 特征添加到 PROP 将使 PROP 使用节点上设置的QRemoteObjectPersistedStore实例(如果有)来保存/恢复 PROP 值。

信号

信号方法是使用 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 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队列连接和 QtRO SIGNALS 中一样,作为引用的槽中的参数在传递给副本时将被复制。

枚举

枚举(在 QtRO 中使用 C++ 枚举和 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}

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 生成的代码为每个POD 创建一个Q_GADGET类,并为POD 定义的每种类型对应的Q_PROPERTY成员。

枚举类型

在类中定义 ENUM 通常更容易和更清晰(参见ENUM),但如果您需要独立的枚举类型,在类定义之外使用 ENUM 关键字可能会有所帮助。这将在您的头文件中生成一个处理编组等的新类。语法与ENUM相同,除了这种情况下的声明不包含在class声明中。

USE_ENUM

USE_ENUM 关键字是在通过 ENUM 关键字自动生成之前实现的。保留它是为了向后兼容。

指令

rep 文件定义了一个接口,但接口通常需要外部元素。为了支持这一点,repc 将在生成的文件的顶部包含任何(单行)指令。例如,这允许您使用支持所需逻辑或数据类型的#include 或#define 指令。

repc 工具目前只包含从“#”符号到行尾的所有内容,并将其添加到生成的文件中。所以不支持多行#if/#else/#endif 语句和多行宏。

项目文件宏

REPC_REPLICA
...REPC_SOURCE
...QOBJECT_REPLICA

这篇关于二 Qt Remote Objects (REPC 编译器)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1077509

相关文章

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点: 鼠标左键双击,设定红色的起点。左键双击设定起点,用红色标记。 设定终点: 鼠标右键双击,设定蓝色的终点。右键双击设定终点,用蓝色标记。 设置障碍点: 鼠标左键或者右键按着不放,拖动可以设置黑色的障碍点。按住左键或右键并拖动,设置一系列黑色障碍点

使用Qt编程QtNetwork无法使用

使用 VS 构建 Qt 项目时 QtNetwork 无法使用的问题 - 摘叶飞镖 - 博客园 (cnblogs.com) 另外,强烈建议在使用QNetworkAccessManager之前看看这篇文章: Qt 之 QNetworkAccessManager踏坑记录-CSDN博客 C++ Qt开发:QNetworkAccessManager网络接口组件 阅读目录 1.1 通用API函数

Qt多语种开发教程

Qt作为跨平台的开发工具,早已应用到各行各业的软件开发中。 今天讲讲,Qt开发的正序怎么做多语言开发。就是说,你设置中文,就中文显示;设置英语就英文显示,设置繁体就繁体显示,设置发育就显示法语等。 开发环境(其实多语种这块根环境没太大关系):win10,Qt.5.12.10 一.先用QtCreator创建一个简单的桌面程序 1.工程就随便命名“LanguageTest”,其他默认。 2.在设计师

Qt中window frame的影响

window frame 在创建图形化界面的时候,会创建窗口主体,上面会多出一条,周围多次一圈细边,这就叫window frame窗口框架,这是操作系统自带的。 这个对geometry的一些属性有一定影响,主要体现在Qt坐标系体系: 窗口当中包含一个按钮,这个按钮的坐标系是以父元素为参考,那么这个参考是widget本体作为参考,还是window frame作为参考,这两种参考体系都存在

【Qt】定时器事件

定时器事件 在之前学习QTimer中实现了定时器的功能,而在QTimer背后是QTimerEvent定时器事件进行支撑的。在QObject中提供了一个timeEvent这个函数。 startTimer启动定时器killTimer关闭定时器 Qt 中在进⾏窗⼝程序的处理过程中,经常要周期性的执⾏某些操作,或者制作⼀些动画效果,使⽤定 时器就可以实现。所谓定时器就是在间隔⼀定时间后,去执⾏某⼀

【linux学习指南】Linux编译器 gcc和g++使用

文章目录 📝前言🌠 gcc如何完成🌉预处理(进行宏替换) 🌠编译(生成汇编)🌉汇编(生成机器可识别代码) 🌠链接(生成可执行文件或库文件)🌉函数库 🌠gcc选项🚩总结 📝前言 预处理(进行宏替换)编译(生成汇编)汇编(生成机器可识别代码)连接(生成可执行文件或库文件) 🌠 gcc如何完成 格式 :gcc [选项] 要编译的文件 [选项] [目标文

QT 编译报错:C3861: ‘tr‘ identifier not found

问题: QT 编译报错:C3861: ‘tr’ identifier not found 原因 使用tr的地方所在的类没有继承自 QObject 类 或者在不在某一类中, 解决方案 就直接用类名引用 :QObject::tr( )

Docker远程连接和Docker Remote Api

在Docker生态系统中一共有3种API:Registry API、Docker Hub API、Docker Remote API 这三种API都是RESTful风格的。这里Remote API是通过程序与Docker进行集成和交互的核心内容。 Docker Remote API是由Docker守护进程提供的。默认情况下,Docker守护进程会绑定到一个所在宿主机的套接字:unix:///v