The server-side mapping for interfaces provides an up-call API for the Ice run time: by implementing virtual functions in a servant class, you provide the hook that gets the thread of control from the Ice server-side run time into your application code.
On the client side, interfaces map to proxy classes (see Section 6.11). On the server side, interfaces map to
skeleton classes. A skeleton is a class that has a pure virtual member function for each operation on the corresponding interface. For example, consider the Slice definition for the
Node interface we defined in
Chapter 5 once more:
In order to provide an implementation for an Ice object, you must create a servant class that inherits from the corresponding skeleton class. For example, to create a servant for the
Node interface, you could write:
By convention, servant classes have the name of their interface with an I‑suffix, so the servant class for the
Node interface is called
NodeI. (This is a convention only: as far as the Ice run time is concerned, you can chose any name you prefer for your servant classes.)
Note that NodeI inherits from
Filesystem::Node, that is, it derives from its skeleton class. It is a good idea to always use virtual inheritance when defining servant classes. Strictly speaking, virtual inheritance is necessary only for servants that implement interfaces that use multiple inheritance; however, the
virtual keyword does no harm and, if you add multiple inheritance to an interface hierarchy half-way through development, you do not have to go back and add a
virtual keyword to all your servant classes.
As far as Ice is concerned, the NodeI class must implement only a single member function: the pure virtual
name function that it inherits from its skeleton. This makes the servant class a concrete class that can be instantiated. You can add other member functions and data members as you see fit to support your implementation. For example, in the preceding definition, we added a
_name member and a constructor. Obviously, the constructor initializes the
_name member and the
name function returns its value:
The name member function of the
NodeI skeleton on
page 278 is not a
const member function. However, given that the operation does not modify the state of its object, it really should be a
const member function. We can achieve this by adding the
["cpp:const"] metadata directive. For example:
Note that readonlyOp is mapped as a
const member function due to the
["cpp:const"] metadata directive; normal and
idempotent operations (without the metadata directive) are mapped as ordinary, non-
const member functions.