Table of Contents Previous Next
Logo
Server-Side Slice-to-C++ Mapping : 8.4 Mapping for Interfaces
Copyright © 2003-2010 ZeroC, Inc.

8.4 Mapping for Interfaces

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 applica­tion code.

8.4.1 Skeleton Classes

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:
module Filesystem {
    interface Node {
        idempotent string name();
    };
    // ...
};
The Slice compiler generates the following definition for this interface:
namespace Filesystem {

    class Node : virtual public Ice::Object {
    public:
        virtual std::string name(const Ice::Current& =
                                    Ice::Current()) = 0;
        // ...
    };
    // ...
}
For the moment, we will ignore a number of other member functions of this class. The important points to note are:
• As for the client side, Slice modules are mapped to C++ namespaces with the same name, so the skeleton class definition is nested in the namespace Filesystem.
• The name of the skeleton class is the same as the name of the Slice interface (Node).
• The skeleton class contains a pure virtual member function for each operation in the Slice interface.
• The skeleton class is an abstract base class because its member functions are pure virtual.
• The skeleton class inherits from Ice::Object (which forms the root of the Ice object hierarchy).

8.4.2 Servant Classes

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:
#include <Filesystem.h> // Slicegenerated header

class NodeI : public virtual Filesystem::Node {
public:
    NodeI(const std::string&);
    virtual std::string name(const Ice::Current&);
private:
    std::string _name;
};
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 hier­archy 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 imple­mentation. 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:
NodeI::NodeI(const std::string& name) : _name(name)
{
}

std::string
NodeI::name(const Ice::Current&) const
{
    return _name;
}

Normal and idempotent Operations

The name member function of the NodeI skeleton on page 291 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:
interface Example {
                void normalOp();

    idempotent  void idempotentOp();

    ["cpp:const"]
    idempotent  void readonlyOp();
};
The skeleton class for this interface looks like this:
class Example : virtual public Ice::Object {
public:
    virtual void normalOp(const Ice::Current&
                                = Ice::Current()) = 0;
    virtual void idempotentOp(const Ice::Current&
                                = Ice::Current()) = 0;
    virtual void readonlyOp(const Ice::Current&
                                = Ice::Current()) const = 0;
    // ...
};
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.

Table of Contents Previous Next
Logo