C++ Quick Start

This guide gets you started with gRPC in C++ with a simple working example.

In the C++ world, there’s no universally accepted standard for managing projectdependencies. In this quick start, you’ll follow steps to build and locallyinstall gRPC before building and running this quick start’s Hello World example.

Setup

Choose a directory to hold locally installed packages. This page assumes thatthe environment variable MY_INSTALL_DIR holds this directory path. Forexample:

  1. $ export MY_INSTALL_DIR=$HOME/local

Ensure that the directory exists:

  1. $ mkdir -p $MY_INSTALL_DIR

Add the local bin folder to your path variable, for example:

  1. $ export PATH="$PATH:$MY_INSTALL_DIR/bin"

Prerequisites

cmake

Version 3.13 or later of cmake is required to install gRPC locally.

  • Linux
  1. $ sudo apt install -y cmake
  • macOS:
  1. $ brew install cmake

Check the version of cmake:

  1. $ cmake --version

Under Linux, the version of the system-wide cmake can be too low. You caninstall a more recent version into your local installation directory as follows:

  1. $ wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.17.0/cmake-3.17.0-Linux-x86_64.sh
  2. $ sh cmake-linux.sh -- --skip-license --prefix=$MY_INSTALL_DIR
  3. $ rm cmake-linux.sh

gRPC and Protocol Buffers

While not mandatory, gRPC applications usually leverageProtocol Buffersfor service definitions and data serialization, and the example code usesproto3.

The following instructions will locally install gRPC and Protocol Buffers.

  • Install the basic tools required to build gRPC:

    • Linux
  1. $ sudo apt install -y build-essential autoconf libtool pkg-config
  • macOS:
  1. $ brew install autoconf automake libtool pkg-config
  • Clone the grpc repo and its submodules:
  1. $ git clone --recurse-submodules -b v1.28.1 https://github.com/grpc/grpc
  2. $ cd grpc
  • Build and locally install gRPC and all requisite tools:
  1. $ mkdir -p cmake/build
  2. $ pushd cmake/build
  3. $ cmake -DgRPC_INSTALL=ON \
  4. -DgRPC_BUILD_TESTS=OFF \
  5. -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
  6. ../..
  7. $ make -j
  8. $ make install
  9. $ popd

More information:

Build the example

The example code is part of the grpc repo source, which you cloned as part ofthe steps of the previous section.

  • Change to the example’s directory:
  1. $ cd examples/cpp/helloworld
  • Build the example using cmake:
  1. $ mkdir -p cmake/build
  2. $ pushd cmake/build
  3. $ cmake -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR ../..
  4. $ make -j

Note

Getting build failures? Most issues, at this point, are a result of afaulty installation. Ensure that the have the right versions of cmake, andcarefully recheck your installation.

Try it!

Run the example from the example build directoryexamples/cpp/helloworld/cmake/build:

  • Run the server:
  1. $ ./greeter_server
  • From a different terminal, run the client and see the client output:
  1. $ ./greeter_client
  2. Greeter received: Hello world

Congratulations! You’ve just run a client-server application with gRPC.

Update a gRPC service

Now let’s look at how to update the application with an extra method on theserver for the client to call. Our gRPC service is defined using protocolbuffers; you can find out lots more about how to define a service in a .protofile inWhat is gRPC? andgRPC Basics:C++. For now all you need to know is that both theserver and the client stub have a SayHello() RPC method that takes aHelloRequest parameter from the client and returns a HelloResponse from theserver, and that this method is defined like this:

  1. // The greeting service definition.
  2. service Greeter {
  3. // Sends a greeting
  4. rpc SayHello (HelloRequest) returns (HelloReply) {}
  5. }
  6. // The request message containing the user's name.
  7. message HelloRequest {
  8. string name = 1;
  9. }
  10. // The response message containing the greetings
  11. message HelloReply {
  12. string message = 1;
  13. }

Editexamples/protos/helloworld.proto and add a new SayHelloAgain() method, with thesame request and response types:

  1. // The greeting service definition.
  2. service Greeter {
  3. // Sends a greeting
  4. rpc SayHello (HelloRequest) returns (HelloReply) {}
  5. // Sends another greeting
  6. rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
  7. }
  8. // The request message containing the user's name.
  9. message HelloRequest {
  10. string name = 1;
  11. }
  12. // The response message containing the greetings
  13. message HelloReply {
  14. string message = 1;
  15. }

Remember to save the file!

Regenerate gRPC code

Before you can use the new service method, you need to recompile the updatedproto file.

From the example build directory examples/cpp/helloworld/cmake/build, run:

  1. $ make -j

This regenerates helloworld.pb.{h,cc} and helloworld.grpc.pb.{h,cc}, whichcontains the generated client and server classes, as well as classes forpopulating, serializing, and retrieving our request and response types.

Update and run the application

You have new generated server and client code, but you still need to implementand call the new method in the human-written parts of our example application.

Update the server

Open greeter_server.cc from the example’s root directory. Implement the newmethod like this:

  1. class GreeterServiceImpl final : public Greeter::Service {
  2. Status SayHello(ServerContext* context, const HelloRequest* request,
  3. HelloReply* reply) override {
  4. // ...
  5. }
  6. Status SayHelloAgain(ServerContext* context, const HelloRequest* request,
  7. HelloReply* reply) override {
  8. std::string prefix("Hello again ");
  9. reply->set_message(prefix + request->name());
  10. return Status::OK;
  11. }
  12. };

Update the client

A new SayHelloAgain() method is now available in the stub. We’ll follow thesame pattern as for the already present SayHello() and add a newSayHelloAgain() method to GreeterClient:

  1. class GreeterClient {
  2. public:
  3. // ...
  4. std::string SayHello(const std::string& user) {
  5. // ...
  6. }
  7. std::string SayHelloAgain(const std::string& user) {
  8. // Follows the same pattern as SayHello.
  9. HelloRequest request;
  10. request.set_name(user);
  11. HelloReply reply;
  12. ClientContext context;
  13. // Here we can use the stub's newly available method we just added.
  14. Status status = stub_->SayHelloAgain(&context, request, &reply);
  15. if (status.ok()) {
  16. return reply.message();
  17. } else {
  18. std::cout << status.error_code() << ": " << status.error_message()
  19. << std::endl;
  20. return "RPC failed";
  21. }
  22. }

Finally, invoke this new method in main():

  1. int main(int argc, char** argv) {
  2. // ...
  3. std::string reply = greeter.SayHello(user);
  4. std::cout << "Greeter received: " << reply << std::endl;
  5. reply = greeter.SayHelloAgain(user);
  6. std::cout << "Greeter received: " << reply << std::endl;
  7. return 0;
  8. }

Run!

Run the client and server like you did before. Execute the following commandsfrom the example build directory examples/cpp/helloworld/cmake/build:

  • Build the client and server after having made changes:
  1. $ make -j
  • Run the server:
  1. $ ./greeter_server
  • On a different terminal, run the client:
  1. $ ./greeter_client

You’ll see the following output:

  1. Greeter received: Hello world
  2. Greeter received: Hello again world

What’s next