Skip to main content
Skip table of contents

Writing a Greeter Client in C++

This page presents how to create a C++ client that that makes requests to a server that is hosting an Ice object which implements the greeter interface.

The client we're going to write is simple; it creates a proxy to a greeter server, and invokes the greet operation on it.

Client Implementation

The structure of our client is going to look like:

CPP
#include "../../common/Env.h"
#include "Greeter.h"

#include <Ice/Ice.h>
#include <future>
#include <iostream>

using namespace std;

int
main(int argc, char* argv[])
{
    // ...
}

Before anything else, we need to include a few header files:

  • Env.h: So we can retrieve the current user's username to pass to greet (not usually necessary).

  • Greeter.h: The header file the Slice compiler generated from Greeter.ice.

  • Ice.h: This header provides definitions that are necessary for accessing the Ice runtime.

  • future: For when we call greet asynchronously using futures.

  • iostream: So we can use cout to print to the console.

We also add a using declaration for the std namespace to reduce clutter.

Then we get to the interesting part: the main function which will run the client logic.
It can be broken down into 3 logical pieces.

First, we create a Communicator and place it in a CommunicatorHolder, which ensures it will be destroyed when it goes out of scope:

CPP
Ice::CommunicatorPtr communicator = Ice::initialize(argc, argv);
Ice::CommunicatorHolder communicatorHolder{communicator};

Next, we create a proxy to the 'greeter' object. For this example, we assume that the server runs on localhost and listens using the default transport (TCP) on port 4061.

CPP
VisitorCenter::GreeterPrx greeter{communicator, "greeter:tcp -h localhost -p 4061"};

Lastly, we can make our invocations to the greeter server. Yep, that’s all it takes!

In this example we show all three ways the client could invoke greet with Ice for C++: a synchronous call, an asynchronous call using futures, and an asynchronous call using callbacks.

The synchronous API is really only provided for convenience. Remote invocations are inherently asynchronous by nature, so it’s best practice to use calls which reflect that.

Synchronous Invocation

Invoking greet synchronously means the client sends the request, and the call will block until a response is received from the server. Here's what it looks like:

CPP
string greeting = greeter->greet(Env::getUsername());
cout << greeting << endl;

Here, we use Env to pass the current username into greet, but as you’ll see, it can be any string.

Asynchronous Invocation

Using asynchronous invocations keeps your threads free to run other code while waiting for the server’s response. Ice for C++ has 2 different APIs for making asynchronous invocations:

CPP
// Future based API

future<string> futureGreeting = greeter->greetAsync("alice"); // Send the request.
// Free to run other code in between...
greeting = futureGreeting.get(); // Block until a respond is received.
cout << greeting << endl;
CPP
// Callback based API

promise<void> promise;
greeter->greetAsync(
    "bob",
    [&promise](string_view greeting) // response callback
    {
        cout << greeting << endl;
        promise.set_value();
    },
    [&promise](std::exception_ptr exceptionPtr) // exception callback
    { promise.set_exception(exceptionPtr); });

// Wait for the response/exception callback to be called.
promise.get_future().get();
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.