The mapping of Slice interfaces revolves around the idea that, to invoke a remote operation, you call a member function on a local class instance that is a proxy for the remote object. This makes the mapping easy and intuitive to use because making a remote procedure call is no different from making a local procedure call (apart from error semantics).
On this page:
Proxy Classes and Proxy Handles
On the client side, a Slice interface maps to a class with member functions that correspond to the operations on that interface. Consider the following simple interface:
The Slice compiler generates the following definitions for use by the client:
Your client code interacts directly with the proxy class,
M::SimplePrx in the example above. More generally, the generated proxy class for an interface in module
M is the C++ proxy class
In the client's address space, an instance of the proxy class is the local ambassador for a remote instance of the
Simple interface in a server and is known as a proxy class instance. All the details about the server-side object, such as its address, what protocol to use, and its object identity are encapsulated in that instance.
All generated proxy classes inherit directly or indirectly from the
Ice::ObjectPrx proxy class, reflecting the fact that all Slice interfaces implicitly inherit from
Inheritance relationships among Slice interfaces are maintained in the generated C++ classes. For example:
The generated code for
CPrx reflects the inheritance hierarchy:
Given a proxy for
C, a client can invoke any operation defined for interface
C, as well as any operation inherited from
C's base interfaces.
Client-side application code never manipulates proxy class instances directly. In fact, you are not allowed to instantiate a proxy class directly. The following code will not compile:
Proxy instances are always instantiated on behalf of the client by the Ice run time, so client code never has any need to instantiate a proxy directly. When the client receives a proxy from the run time, it is given
The client accesses the proxy via this
shared_ptr takes care of forwarding operation invocations to its underlying proxy, as well as reference-counting the proxy. This means that no memory-management issues can arise: deallocation of a proxy is automatic and happens once the last
shared_ptr to the proxy disappears (goes out of scope).
Down-casting Proxies with
The Ice namespaces provides two template functions,
uncheckedCast, modeled after
These functions allow you to down-cast a proxy to a more derived proxy type.
A checked cast has the same function for proxies as a C++
dynamic_cast has for pointers: it allows you to assign a base proxy to a derived proxy. If the type of the base proxy's target object is compatible with the derived proxy's static type, the assignment succeeds and, after the assignment, the derived proxy denotes the same remote object as the base proxy. Otherwise, if the type of the base proxy's target object is incompatible with the derived proxy's static type, the derived proxy is set to null. Here is an example to illustrate this:
checkedCast<DerivedPrx>(base) tests whether
base points at a remote object of type
Derived (or an object with a type that is derived from
Derived). If so, the cast succeeds and
derived is set to point at the same object as
base. Otherwise, the cast fails and
derived is set to null.
checkedCast always results in a remote message,
ice_isA, to the server. The message effectively asks the server "is the object denoted by this proxy of type Derived?". When
ice_isA returns true,
checkedCast manufactures and returns a new proxy instance.
Sending a remote message is necessary because, as a rule, there is no way for the client to find out what the actual run-time type of a remote Ice object is without confirmation from the server. (For example, the server may replace the implementation of the object for an existing proxy with a more derived one.) This means that you have to be prepared for a
checkedCast to fail. For example, if the server is not running, you will receive a
ConnectFailedException; if the server is running, but the object denoted by the proxy no longer exists, you will receive an
In some cases, it is known that an object supports a more derived interface than the static type of its proxy. For such cases, you can use an unchecked down-cast. An
uncheckedCast provides a down-cast without consulting the server as to the actual run-time type of the object, for example:
You should use an
uncheckedCast only if you are certain that target object indeed supports the more derived type: an
uncheckedCast, as the name implies, is not checked in any way; it does not contact the object in the server and never fails. If you use the proxy resulting from an incorrect
uncheckedCast to invoke an operation, the behavior is undefined. Most likely, you will receive an
OperationNotExistException, but, depending on the circumstances, the Ice run time may also report an exception indicating that unmarshaling has failed, or even silently return garbage results.
heckedCast on a proxy that is already of the desired proxy type returns immediately that proxy. Otherwise, unc
heckedCast creates a new instance of the desired proxy class.
Despite its dangers,
uncheckedCast is useful because it avoids the cost of sending a message to the server. And, particularly during initialization, it is common to receive a proxy of static type
Ice::ObjectPrx, but with a known run-time type. In such cases, an
uncheckedCast saves the overhead of sending a remote message.
Typed Proxy Factory Methods in C++
The base proxy class
ObjectPrx supports a variety of methods for customizing a proxy. Since proxies are immutable, each of these "factory methods" returns a copy of the original proxy that contains the desired modification. For example, you can obtain a proxy configured with a ten second invocation timeout as shown below:
A factory method returns a new proxy object if the requested modification differs from the current proxy, otherwise it returns the current proxy. With few exceptions, the corresponding C++ factory member functions return a proxy of the same type as the current proxy, therefore it is generally not necessary to down-cast after calling such a factory. The example below demonstrates these semantics:
The only exceptions are the factory member functions
ice_identity. Calls to either of these functions may produce a proxy for an object of an unrelated type, therefore they return a base proxy that you must subsequently down-cast to an appropriate type.
Object Identity and Proxy Comparison in C++
You can compare proxies for equality. By default, proxy comparison compares all aspects of a proxy, including the object identity, facet name, addressing information, and all the proxy settings; two proxies compare equal only if they are identical in all respects. The mapping provides helper functions to simplify the comparison of proxies stored in
Note however that the more common use case is determining whether two proxies denote the same Ice object, in which case you should only be comparing their object identities. To compare the object identities of two proxies, you can use helper functions and classes in the
proxyIdentityEqual function returns true if the object identities embedded in two proxies are the same and ignores other information in the proxies, such as facet and transport information. To include the facet name in the comparison, use
proxyIdentityLess function establishes a total ordering on proxies. It is provided mainly so you can use object identity comparison with sorted containers. (The function uses
name as the major ordering criterion, and
category as the minor ordering criterion.) The
proxyIdentityAndFacetLess function behaves similarly to
proxyIdentityLess, except that it also compares the facet names of the proxies when their identities are equal.