I wrote the first 10 parts of this series in 2023. In December 2023 I started working on an application I have been thinking about for a decade. It's a Personal Organizer using the "Getting Things Done" approach + some other ideas. The application is designed to work on PC and Mobile, using a server to keep track of everything. The idea is that I plan my time using the PC's comfortable big monitor and keyboard, and then switch to a laptop or mobile phone when I'm on the road. So, basically it use an event-driven MVC pattern where the model resides on a server. gRPC is perfect for something like this. I can use simple RPC methods to add and change data, and a stream from the server with all the changes from all the active client devices.
I am just a single guy, with no VC funding or big corporation sponsoring my work. So I need to re-use the code for the UI application on PC and Mobile. For this, QT is great. It let me write a C++ UI application that can run on PC's (Linux, MacBook, Windows) and Mobile (Android, IOS) with just minor differences to adjust to the different platforms.
In the past I have played with QT and gRPC. That was before I invested some time in understanding how to work efficiently with gRPC - but the biggest pain-point was that QT use QString while gRPC use std::string. So in addition to handle the communication, I also had to convert the data in both directions. No fun at all!
What I discovered in December 2023 was that QT was working on adding gRPC support to their tooling. They already had their own code generator and CMake macros to serialize protobuf directly to C++ classes, using their familiar classes, like QString and QList. It was pretty much undocumented at the time. But I with a little help from the "Qt Forum", I was able to use it.
In June 2024, they released QT 6.8-beta1, with a much more mature version of the gRPC tooling. If you use gRPC with your QT app, I would definitely recommend that you take a closer look at it. It's really great, and much simpler to use than the standard protoc generated code.
The protobuf classes they generate are derived from QObject. Each property is a full property with getters and setters and signals to notify changes. They can be used directly in QML. In edit dialogs, in my own code, I now get a instance of data received from the server, do changes to it, and send it back to my C++ code who after some validation sends it back to the server. It makes it really convenient and fast to write the code for the apps.
So, lets take a closer look at how it works. I have written a new example for the "Route Guide" interface in QT, using their gRPC support. The application has a very simple C++ class; ServerComm, and a simple QML UI.
Lets start with CMake
First we must tell CMake what modules we will use. For gRPC, we need Protobuf, ProtobufQuick and Grpc.
SET QTP0001 NEW) QML QML_URI PROTO_FILES )) ) PRIVATE > > > > ) ) URI VERSION QML_FILES RESOURCES SOURCES ) PRIVATE ) PRIVATE )Note the new qt_add_protobuf and qt_add_grpc macros. For the app, we link with the targets these create.
In the main.cpp file, we just start the QML engine, like in any other QML application.
QQmlApplicationEngine engine; engine.; if LOG_ERROR << "Failed to initialize engine!"; return -1; } return app.;The ServerComm class is very simple. It's basically a naive QML enabled class with a few properties and a few methods we can call from QML to test the four gRPC methods in "Route Guide".
Then I have added a simple template to make calling normal (unary) gRPC methods even simpler.
// Simple template to hide the complexity of calling a normal gRPC method.// It takes a method to call with its arguments and a functor to be called when the result is ready.void Finally, the class has some members for the gRPC stream (connection) and two of the streams.
routeguide::RouteGuide::Client client_;std::shared_ptr<QGrpcClientStream> recordRouteStream_;std::shared_ptr<QGrpcBidirStream> routeChatStream_;In the constructor, we subscribe for comm errors from gRPC.
: ;}In the start() function we prepare to connect the client to the gRPC server we specify in the UI.
We could have added properties to the channelOptions below, for example, for authentication or session information.
void One nice thing with the the code above, is that it can be called again and again if you lost the connection with the server, or if you want to connect to another server.
Note that we get no errors if the server address is invalid or there are other problems, until we call a gRPC method.
Below is GetFeature. It is async and the lambda is called in the main thread when the RPC call is complete.
void To use a stream from the server, we first create an async stream by calling the appropriate gRPC method. Then we subscribe to QGrpcServerStream::messageReceived and QGrpcServerStream::finished.
void For the outgoing stream, we start by creating it by calling the appropriate gRPC method.
We also connect to QGrpcClientStream::finished to get the message the server sends to us after we have told it that we are done sending messages.
void Then we need a method that out UI can call to send one message to the server.
void And finally for RouteUpdate, we need a method to tell the server that we are done.
void For RouteChat, we combine the pattern for the last two methods. We create a bidirectional stream by calling the appropriate gRPC method. Then we subscribe for incoming messages and the finished signal. For outgoing messages and to end the chat, we implement two methods as above.
First we create the stream and subscribe the the relevant events:
void Then we add a method to send a message.
void And finally we add a message to tell the server that we are done chatting with her.
void The error handler in our implantation is quite simple. It just logs the error, set the status text in the UI and triggers a signal to make the UI disable the data buttons.
void That's all for basic use. Much simpler, in my opinion, than the interfaces and stubs generated by protoc.
The full code for the QT project is here: qt_client
For a more extensive example of how I use gRPC in a real QT Desktop/Mobile application, you can look at Next-app UI.