Nur ein weiterer Qt-Wrapper für gRPC und Protobuf



Vor nicht allzu langer Zeit war ich verwirrt darüber, dass es nicht genügend praktische und einfache Wrapper und Generatoren für Protobuf und gRPC gibt, die auf Qt basieren und vollständig mit Qt kompatibel sind. Ich bin auf Artikel gestoßen, darunter hier über Wrapper, aber ihre Verwendung schien mir viel weniger praktisch als selbst die vorhandene C ++ - API.

Ein bisschen über gRPC und Protobuf


Simulieren wir eine Situation: Sie schreiben ein plattformübergreifendes Projekt und müssen ein RPC-Framework für die Kommunikation mit Ihren Diensten auswählen. Sie können sich immer in die Brust schlagen und sagen: „Ich bin mein eigener Rahmen“, aber es scheint mir, dass wir in einer Ära vorgefertigter Lösungen leben. Eine solche Lösung wurde uns lange Zeit von einem bekannten Unternehmen vorgestellt. Ich nehme nicht an, RPC-Frameworks zu vergleichen, dies ist nicht der Zweck dieses Artikels. Nur um aufzulisten, was mir an gRPC gefällt:

  • Prägnante und klare IDL
  • Das Vorhandensein einer großen Anzahl von Generatoren für verschiedene Plattformen
  • Generierter Client / Server-Code für schnelles und einfaches Prototyping und Schreiben von Testanwendungen

Auf den Punkt


Aufgrund der Tatsache, dass Qt mit der Typreflexion recht gut zurechtkommt und die Menge an Metainformationen im Allgemeinen auf dem höchsten Niveau liegt, wurde klar, dass Sie einen eigenen Generator benötigen, der „reinen“ Qt-Code generiert, ohne Bibliotheken von Drittanbietern zu verteilen. So wurde qtprotobufgen geboren.

qtprotobufgen


qtprotobufgen ist der einfachste Generator, der auf der von libprotoc bereitgestellten API basiert. Für den Fall, dass Sie so etwas für Ihre Bedürfnisse machen möchten, werde ich einen kleinen Betrug hinterlassen.

  • Sie haben einen einzigen Einstiegspunkt in die Plugin-Klasse :: google :: protobuf :: compiler :: CodeGenerator, von der Sie erben müssen
  • Virtuelle Methode generieren bestimmt die Generierung, wenn mit einer separaten .proto-Datei gearbeitet wird
  • Die virtuelle GenerateAll-Methode bestimmt die Generierung, wenn mit einem vollständigen Array von .proto-Dateien gearbeitet wird, die für die Generierung bereitgestellt werden oder Abhängigkeiten sind
  • Die virtuelle HasGenerateAll-Methode ist im Wesentlichen ein Relikt, das aus früheren Versionen erhalten geblieben ist. Geben Sie true zurück

Ich muss sofort sagen, dass es keinen Wunsch gab, meinen eigenen Parser / Generator von Grund auf neu zu schreiben, da es eine fertige Lösung von Protobuf-Entwicklern gibt. Wenn Sie möchten, können Sie den von protoc ausgegebenen Binärdatenstrom lesen oder einen eigenen Parodatei-Parser schreiben.

Während der Entwicklung trat ein wesentlicher Nachteil eines in einer kompilierten Sprache geschriebenen Generators auf: Es war schwierig, die Generierung und Kompilierung in einem CMake-Stapel zusammenzufassen. Aufgrund der Tatsache, dass Qt die Generierung von Metaobjektinformationen basierend auf Header-Dateien mit dem Makro Q_OBJECT im Hauptteil der in der Header-Datei deklarierten Klassen durchführt, ist es in der Konfigurationsphase (cmake lesen) erforderlich, eine Vorstellung von den Dateien zu haben, die moc für die weitere Codegenerierung bereitstellt. Als Lösung musste ich auf die interpretierte Sprache Go (Lang) zurückgreifen, die keine zusätzlichen Abhängigkeiten erzeugte und ihre Arbeit perfekt erledigte, aber nicht genügend Tests bestand.

Der Generator unterliegt den bestehenden Protokollregeln und führt zum Zeitpunkt des Schreibens keine zusätzlichen Generierungsoptionen ein:

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

Zur Vereinfachung und Benutzerfreundlichkeit können Sie speziell vorbereitete cmake-Routinen verwenden, um Code zu generieren und in ein cmake-Projekt einzubetten. Weitere Details ...

Über Bibliotheken


Ich sehe nicht viel Sinn darin, die API im Detail zu beschreiben. Wer möchte, kann Dokumentation erstellen und etwas mehr über die derzeit verfügbare API lesen.

Das Projekt ist in zwei logische Teile unterteilt: qtprotobuf und qtgrpc. Aus den Namen denke ich, dass der Zweck jeder Komponente klar ist. Wir haben versucht, die Verwendung so bequem wie möglich zu gestalten, da es Integrationsoptionen sowohl für die vormontierte und im System installierte Bibliothek als auch für die Integration des Teilprojekts in Ihr cmake-Projekt gibt.

Der generierte Code wird vollständig * in QML exportiert, was die Arbeit mit der gRPC-API erheblich erleichtert.

Verwenden Sie


Nach der Integration in das Projekt und der Durchführung der Generierung erhalten Sie eine Reihe von Quelldateien, die später in einer statischen Bibliothek gesammelt und mit Ihrer Binärdatei verknüpft werden. Jüngste Änderungen schlossen die Möglichkeit einer statischen Registrierung von generierten und Prototypen aus. Daher müssen Sie sich um deren Registrierung im Projekt kümmern:

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

Zum Zeitpunkt des Schreibens gibt es keine einzige Methode zum Registrieren aller für ein Protopaket generierten Typen. Daher müssen Sie die qRegisterProtobufType-Methode für alle in der Anwendung verwendeten Typen aufrufen:

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

Die Verwendung von Bibliotheken und eines Generators wird in README beschrieben, und einige Beispiele begleiten das Projekt. Für diejenigen, die mit gRPC / protobuf überhaupt nicht vertraut sind, empfehle ich, dass Sie die offizielle Dokumentation lesen

Für Entwickler


Wir haben versucht, TDD während der Entwicklung einzuhalten und wollen nicht davon abweichen. Wie unsere Erfahrung gezeigt hat, spart TDD Sie beim Umgestalten oder Aktualisieren der API und hilft dabei, versteckte Probleme zu erkennen. Wenn Sie einen Beitrag leisten möchten, sollten Sie daher bereit sein, Einheiten, Einheiten- und Funktionstests zu schreiben.

* Bekannte Probleme


Derzeit gibt es eine Reihe von Qt-bezogenen Problemen. Einige von ihnen wurden mit unserer oder ohne unsere Teilnahme gelöst, aber nicht alle waren in den aktuellen Qt-Versionen enthalten. Das wichtigste ist die Unzugänglichkeit einiger grundlegender Protobuf-Typen aus qml-Code. Ich denke, es ist für niemanden ein Geheimnis, dass die Anzahl der in QML verfügbaren Typen sehr begrenzt ist, auch aufgrund der Verwendung von V8 als JS-Engine. Ein Versuch, QML für angepasste Typen (z. B. fixed32, sint32) ein wenig benutzerfreundlicher zu gestalten, schlug fehl, stellte jedoch die Ursache des Problems fest . Die aktuelle Implementierung von QtNetwork weist ebenfalls eine Reihe von Problemen auf, die vom Qt-Team jedoch schnell behoben werden.
QTBUG-77852
QTBUG-76303
QTBUG-78310

Pläne


Alle aktuellen Aktivitäten beziehen sich auf die Fehlerbehebung im Projektcode oder im Qt-Code. Mit der neuen Funktionalität ist jedoch ziemlich viel Arbeit verbunden:

  1. Übergang zu einem einzelnen Paar von .h / .cpp-Dateien für den generierten Code
  2. GRPC-Server-Implementierung
  3. Recycling-API für gRPC-Anmeldeinformationen
  4. Verteilung des generierten Codes in Verzeichnisse und Erstellung von Teilprojekt-Plug-Ins zum separaten Laden generierter Pakete und Module
  5. Qmake Integration
  6. CI-Implementierung

Es gibt einen Rückstand, der immer noch in einem eigenen Projekt-Repository gespeichert ist.

Anstelle einer Schlussfolgerung möchte ich mich bei den Kameraden von PVS-Studio für den bereitgestellten Schlüssel für OSS-Projekte bedanken. Mit ihrer Hilfe fanden sie einen ziemlich kritischen Fehler im generierten Code.

Laden Sie das Projekt herunter, sehen Sie es sich an und spielen Sie hier mit Beispielen.

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


All Articles