只是gRPC和protobuf的另一个Qt包装器



不久前,我感到困惑的是,基于protobuf和gRPC的,与Qt完全兼容的便利和简单的包装器和生成器还不够。 我碰到过很多文章,包括 关于包装器,但是在我看来,它们的使用甚至比现有的C ++ API还不实用。

关于gRPC和protobuf的一些知识


让我们模拟一个情况:您将要编写一个多平台项目,并且需要选择一个RPC框架与服务进行通信。 您总是可以自拔,说“我是我自己的框架”,但在我看来,我们正处在现成解决方案的时代。 这种解决方案之一是由一家知名公司长期提供给我们的。 我不假定要比较RPC框架,这不是本文的目的。 只是列出我喜欢的gRPC:

  • 简洁明了的IDL
  • 大量用于各种平台的发电机
  • 生成的客户端/服务器代码,用于快速轻松地进行原型设计和编写测试应用程序

要点


由于Qt在类型反射方面做得很好,并且元信息的数量通常处于最高水平,因此意识到您需要自己的生成器来生成“纯” Qt代码,而无需散布第三方库。 因此,qtprotobufgen诞生了。

甲胎蛋白


qtprotobufgen是最简单的固有生成器,它基于libprotoc提供的API。 如果您想根据自己的需要制作类似的东西,我会作弊。

  • 您只有一个插件类的入口点::: google :: protobuf ::编译器:: CodeGenerator,您需要从中继承
  • 生成虚拟方法确定使用单独的.proto文件时的生成
  • 当使用完整的.proto文件数组提供生成或依赖关系时,虚拟的GenerateAll方法确定生成
  • 虚拟的HasGenerateAll方法本质上是先前版本中遗留下来的遗物。 返回真

我必须马上说,因为protobuf开发人员有现成的解决方案,所以不想从头开始编写自己的解析器/生成器。 但是,如果您愿意,您可以读取存在协议问题的二进制流,或者编写自己的原始文件解析器。

在开发过程中,以编译语言编写的生成器的一个重大缺点浮出水面:很难将生成和编译放在一个CMake堆栈中。 由于Qt会根据标头文件中声明的类的主体中具有Q_OBJECT宏的标头文件生成元对象信息,因此在配置阶段(读取cmake)有必要了解moc将为进一步代码生成提供的文件。 作为解决方案,我不得不求助于解释语言Go(Lang),该语言没有创建其他依赖项,并且可以很好地完成其工作,但是没有通过足够的测试。

生成器要遵守现有的协议规则,并且在编写本文时,不会引入任何其他生成选项:

protoc --plugin=protoc-gen-qtprotobuf=<path/to/bin>/qtprotobufgen --qtprotobuf_out=<output_dir> <protofile>.proto [--qtprotobuf_opt=out=<output_dir>] 

为了简单易用,您可以使用专门准备的cmake例程来生成代码,并将其嵌入到cmake项目中。 更多细节...

关于图书馆


在详细描述API方面,我没有多大意义。 那些希望的人可以生成文档并阅读更多有关当前可用API的信息。

该项目分为2个逻辑部分qtprotobuf和qtgrpc。 从名称来看,我认为每个组件的目的都很明确。 我们尝试使使用尽可能方便,因为对于在系统中预先组装和安装的库,以及将子项目集成到cmake项目中,都有集成选项。

生成的代码完全*导出到QML,这使得使用gRPC API更加容易。

使用方法


与项目集成并执行生成后,您将收到一组源文件,这些源文件随后将收集在静态库中并链接到您的二进制文件。 最近的更改排除了生成和原型静态注册的可能性。 因此,您需要注意他们在项目中的注册:

 ... #include <QtProtobufTypes> ... int main(int argc, char *argv[]) { QtProtobuf::registerProtoTypes(); ... //   Qt  } 

在撰写本文时,没有用于注册为原型包生成的所有类型的单一方法,因此您需要为应用程序中使用的所有类型调用qRegisterProtobufType方法:

 ... qRegisterProtobufType<MyProtoType>(); ... 

自述文件中描述了库和生成器的用法,并且该项目附带了几个示例。 对于那些根本不熟悉gRPC / protobuf的人,建议您阅读官方文档

对于开发人员


我们试图在开发过程中遵守TDD,并且不想偏离它。 正如我们的经验所示,TDD在重构或更新API时可以为您省钱,它有助于检测隐藏的问题。 因此,如果希望有所贡献,请准备编写单元,单元和功能测试。

*已知问题


当前存在许多与Qt相关的问题。 在我们的参与下或没有我们的参与下,其中一些已解决,但并非所有都包含在当前的Qt版本中。 主要的问题是qml代码无法访问某些基本的protobuf类型。 我认为对于QML中可用的类型集非常有限的人来说,这并不是什么秘密,部分原因是因为使用V8作为JS引擎。 试图使QML对自定义类型(例如,fixed32,sint32)更加友好的尝试失败了,但结果却解决了问题的根源。 QtNetwork的当前实现也存在许多问题,但是Qt团队迅速解决了这些问题。
QTBUG-77852
QTBUG-76303
QTBUG-78310

计划


当前的所有活动都与项目代码或Qt代码中的故障排除有关。 但是,与新功能相关的工作量很大:

  1. 转换为一对.h / .cpp文件以生成代码
  2. GRPC服务器实施
  3. 回收gRPC凭证的API
  4. 将生成的代码分发到目录中,并创建子项目插件,以分别加载生成的包和模块
  5. Qmake整合
  6. CI实施

有一些积压,这些积压仍存储在其自己的项目存储库中。

除了结论以外,我还要感谢PVS-Studio的同志为OSS项目提供的密钥。 在他们的帮助下,他们在生成的代码中发现了一个相当关键的错误。

下载,查看项目并在此处试用示例。

Source: https://habr.com/ru/post/zh-CN467893/


All Articles