Initialization and CommunicatorHolder in C++98
On this page:
Initializing the Ice Run Time with Ice::initialize
Every Ice-based application needs to initialize the Ice run time, and this initialization returns an Ice::Communicator
object.
A Communicator
is a local C++ object that represents an instance of the Ice run time. Most Ice-based applications create and use a single Communicator
object, although it is possible and occasionally desirable to have multiple Communicator
objects in the same application or program.
You initialize the Ice run time by calling the C++ function Ice::initialize
, for example:
int main(int argc, char* argv[]) { Ice::CommunicatorPtr communicator = Ice::initialize(argc, argv); // ... }
initialize
accepts a C++ reference to argc
and an argument vector argv
. The function scans the argument vector for any command-line options that are relevant to the Ice run time; any such options are removed from the argument vector so, when initialize
returns, the only options and arguments remaining are those that concern your application. If anything goes wrong during initialization, initialize
throws an exception.
Ice::initialize
has additional overloads to permit other information to be passed to the Ice run time.
initialize
is a low-level function, as you need to explicitly call destroy
on the returned Communicator
object when you're done with Ice, typically just before returning from main
. The destroy
member function is responsible for finalizing the Ice run time. In particular, in a server, destroy
waits for any operation implementations that are still executing to complete. In addition, destroy
ensures that any outstanding threads are joined with and reclaims a number of operating system resources, such as file descriptors and memory.
The general shape of the main
function of an Ice-based application is therefore as follows:
#include <Ice/Ice.h> int main(int argc, char* argv[]) { int status = 0; try { Ice::CommunicatorPtr communicator = Ice::initialize(argc, argv); try { ... application code ... communicator->destroy(); // destroy is noexcept } catch(const std::exception&) { ... // make sure communicator is destroyed if an exception is thrown communicator->destroy(); throw; } } catch(const std::exception& e) { cerr << e.what() << endl; status = 1; } return status; }
This code is a little bit clunky, as we need to make sure the communicator gets destroyed in all paths, including when an exception is thrown. As a result, most of the time, you should not call initialize
directly: you should use instead a helper class that calls initialize
and ensures the resulting communicator gets eventually destroyed.
Ice::CommunicatorHolder
RAII Helper Class
A CommunicatorHolder
is a small RAII helper class that creates a Communicator
in its constructor (by calling initialize)
and destroys this communicator in its destructor.
With a CommunicatorHolder
, our typical main
function becomes much simpler:
#include <Ice/Ice.h> int main(int argc, char* argv[]) { int status = 0; try { Ice::CommunicatorHolder ich(argc, argv); // Calls Ice::initialize ... application code ... // CommunicatorHolder's destructor calls destroy on the communicator // whether or not an exception is thrown } catch(const std::exception& e) { cerr << e.what() << endl; status = 1; } return status; }
Ice::CommunicatorHolder
is defined as follows:
namespace Ice { class CommunicatorHolder { public: CommunicatorHolder(); explicit CommunicatorHolder(int& arrgc, const char* argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION); ... more CommunicatorHolder ctor overloads ... CommunicatorHolder(const CommunicatorPtr&); CommunicatorHolder& operator=(const CommunicatorPtr&); ~CommunicatorHolder(); operator bool() const; const CommunicatorPtr& communicator() const; const CommunicatorPtr& operator->() const; CommunicatorPtr release(); ... }; }
Let's examine each of these functions:
This default constructor creates an empty holder (holds no Communicator).CommunicatorHolder()
CommunicatorHolder(int& argc, const char* argv[], const InitializationData& initData = InitializationData(), int version = ICE_INT_VERSION)
and many more constructorsEach of these constructor calls
initialize
with the provided parameters; the newCommunicatorHolder
then holds the resultingCommunicator
in a private data member (not shown).
CommunicatorHolder(const CommunicatorPtr&)
This constructor adopts the given communicator: theCommunicatorHolder
becomes responsible to calldestroy
on it.
CommunicatorHolder& operator=(const CommunicatorPtr&)
This assignment operator destroys the communicator held by thisCommunicatorHolder
, then adopts the provided communicator.
~CommunicatorHolder()
The destructor callsdestroy
on the communicator held by thisCommunicatorHolder
.
operator bool() const
Returnstrue
when thisCommunicatorHolder
holds aCommunicator
, andfalse
otherwise.
const CommunicatorPtr& communicator() const
This function gives read-only access to the communicator held by thisCommunicatorHolder
.
const CommunicatoPtr& operator->() const
This arrow operator allows you to use a
CommunicatorHolder
just like aCommunicator
object. For example:C++Ice::CommunicatorHolder ich(argc, argv); Ice::ObjectPrx base = ich->stringToProxy("SimplePrinter:default -p 10000");
is equivalent to:
C++Ice::CommunicatorHolder ich(argc, argv); Ice::ObjectPrx base = ich->communicator()->stringToProxy("SimplePrinter:default -p 10000");
CommunicatorPtr release()
This function returns the communicator held byCommunicatorHolder
to the caller, and the caller becomes responsible to destroy this communicator.CommunicatorHolder
no longer holds a communicator after this call.
The default constructor of CommunicatorHolder
does nothing:
Ice::CommunicatorHolder ich; // does not create a Communicator, ich.communicator() returns a null shared_ptr
If you want to create a CommunicatorHolder
that holds a Communicator
created by initialize
with no args, you can write:
Ice::CommunicatorHolder ich = Ice::initialize();