Juste un autre wrapper Qt pour gRPC et protobuf



Il n'y a pas si longtemps, j'ai été intrigué par le fait qu'il n'y ait pas suffisamment de wrappers et de générateurs pratiques et simples pour protobuf et gRPC, basés et entièrement compatibles avec Qt. Je suis tombé sur des articles, notamment ici sur les wrappers, mais leur utilisation me semblait beaucoup moins pratique que même l'API C ++ existante.

Un peu sur gRPC et protobuf


Simulons une situation: vous allez écrire un projet multi-plateforme et vous devez choisir un framework RPC pour communiquer avec vos services. Vous pouvez toujours vous donner un coup de poing dans la poitrine et dire «je suis mon propre cadre», mais il me semble que nous vivons à une époque de solutions toutes faites. Une de ces solutions nous a été présentée depuis longtemps par une entreprise bien connue. Je ne prétends pas comparer les frameworks RPC, ce n'est pas le but de cet article. Juste pour lister ce que j'aime à propos de gRPC:

  • IDL concis et clair
  • La présence d'un grand nombre de générateurs pour différentes plateformes
  • Code client / serveur généré pour le prototypage et l'écriture d'applications de test rapides et faciles

Au point


En raison du fait que Qt se débrouille assez bien avec la réflexion de type, et que la quantité de méta-informations est généralement au plus haut niveau, il s'est rendu compte que vous avez besoin de votre propre générateur qui générerait du code Qt «pur», sans intercaler les bibliothèques tierces. Donc qtprotobufgen est né.

qtprotobufgen


qtprotobufgen est le générateur intrinsèquement le plus simple, basé sur l'API fournie par libprotoc. Si vous voulez faire quelque chose comme ça pour vos besoins, je vais laisser une petite triche.

  • Vous avez un point d'entrée unique pour la classe de plugin :: google :: protobuf :: compiler :: CodeGenerator, dont vous devez hériter
  • Générer une méthode virtuelle détermine la génération lorsque vous travaillez avec un fichier .proto distinct
  • La méthode virtuelle GenerateAll détermine la génération lorsque vous travaillez avec un tableau complet de fichiers .proto fournis pour la génération ou comme dépendances
  • La méthode virtuelle HasGenerateAll est essentiellement une relique survivant des versions précédentes. Retour vrai

Je dois dire tout de suite qu'il n'y avait aucune envie d'écrire mon propre analyseur / générateur à partir de zéro, car il existe une solution toute faite des développeurs de protobuf. Mais si vous le souhaitez, vous pouvez lire le flux binaire qui émet des protocoles ou écrire votre propre analyseur de proto-fichiers.

Pendant le développement, un inconvénient important du générateur écrit dans le langage compilé est apparu: il était difficile de mettre la génération et la compilation dans une pile CMake. Du fait que Qt génère des informations de méta-objets, basées sur des fichiers d'en-tête ayant la macro Q_OBJECT dans le corps des classes déclarées dans le fichier d'en-tête, il est nécessaire au stade de la configuration (lire cmake) d'avoir une idée des fichiers que moc fournira pour la génération de code supplémentaire. Comme solution, j'ai dû recourir au langage interprété Go (Lang), qui n'a pas créé de dépendances supplémentaires et a parfaitement fait son travail, mais n'a pas passé suffisamment de tests.

Le générateur est soumis aux règles de protocole existantes et, au moment de la rédaction, n'introduit aucune option de génération supplémentaire:

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

Pour plus de simplicité et de facilité d'utilisation, vous pouvez utiliser des routines cmake spécialement préparées pour générer du code et les incorporer dans un projet cmake. Plus de détails ...

À propos des bibliothèques


Je ne vois pas grand intérêt à décrire l'API en détail. Ceux qui le souhaitent peuvent générer de la documentation et en savoir un peu plus sur l'API actuellement disponible.

Le projet est divisé en 2 parties logiques qtprotobuf et qtgrpc. D'après les noms, je pense que l'objectif de chaque composant est clair. Nous avons essayé de rendre l'utilisation aussi pratique que possible, car il existe des options d'intégration pour la bibliothèque pré-assemblée et installée dans le système, et l'intégration du sous-projet dans votre projet cmake.

Le code généré est entièrement * exporté vers QML, ce qui facilite grandement l'utilisation de l'API gRPC.

Utiliser


Après avoir intégré le projet et effectué la génération, vous recevrez un ensemble de fichiers sources, qui seront ensuite collectés dans une bibliothèque statique et liés à votre fichier binaire. Les modifications récentes ont exclu la possibilité d'un enregistrement statique des types générés et des prototypes. Par conséquent, vous devez vous occuper de leur inscription dans le projet:

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

Au moment de l'écriture, il n'y a pas de méthode unique pour enregistrer tous les types générés pour un proto-package, vous devez donc appeler la méthode qRegisterProtobufType pour tous les types utilisés dans l'application:

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

L'utilisation de bibliothèques et d'un générateur est décrite dans README, et quelques exemples accompagnent le projet. Pour ceux qui ne sont pas du tout familiers avec gRPC / protobuf, je vous suggère de lire la documentation officielle

Pour les développeurs


Nous avons essayé d'adhérer à TDD pendant le développement et nous ne voulons pas en dévier. Comme notre expérience l'a montré, TDD vous évite lors de la refactorisation ou de la mise à jour de l'API, il aide à détecter les problèmes cachés. Par conséquent, s'il y a un désir de contribuer, soyez prêt à écrire des unités, des tests unitaires et fonctionnels.

* Problèmes connus


Il existe actuellement un certain nombre de problèmes liés à Qt. Certains d'entre eux ont été résolus, avec le nôtre ou sans notre participation, mais tous n'ont pas été inclus dans les versions actuelles de Qt. Le principal est l'inaccessibilité de certains types de protobuf de base à partir du code qml. Je pense que ce n'est un secret pour personne que l'ensemble des types disponibles dans QML est très limité, en partie à cause de l'utilisation de V8 comme moteur JS. Une tentative pour rendre QML un peu plus convivial pour les types personnalisés (par exemple, fixed32, sint32) a échoué, mais il s'est avéré corriger la source du problème . L'implémentation actuelle de QtNetwork présente également un certain nombre de problèmes, mais l'équipe Qt les résout rapidement.
QTBUG-77852
QTBUG-76303
QTBUG-78310

Plans


Toutes les activités en cours sont liées au dépannage dans le code de projet ou dans le code Qt. Mais il y a une quantité assez importante de travail associée à la nouvelle fonctionnalité:

  1. Transition vers une seule paire de fichiers .h / .cpp pour le code généré
  2. Implémentation du serveur GRPC
  3. API de recyclage pour les informations d'identification gRPC
  4. Distribution du code généré dans les répertoires et création de plugins de sous-projets pour le chargement séparé des packages et modules générés
  5. Intégration Qmake
  6. Implémentation CI

Il existe un certain retard, qui est toujours stocké dans son propre référentiel de projet.

Au lieu d'une conclusion, je voudrais remercier les camarades de PVS-Studio pour la clé fournie pour les projets OSS. Avec leur aide, ils ont trouvé un bogue assez critique dans le code généré.

Téléchargez, voyez le projet et jouez avec des exemples ici .

Source: https://habr.com/ru/post/fr467893/


All Articles