Dynamic Invocation

This page describes the language mappings for the ice_invoke proxy function (on ObjectPrx).

On this page:

Synchronous Mapping for ice_invoke

The mapping for ice_invoke follows the standard rules for each language mapping.

ice_invoke API

The synchronous mapping for ice_invoke on ObjectPrx is shown below:

bool ice_invoke(const std::string& operation,
                Ice::OperationMode mode,
                const std::vector<Ice::Byte>& inParams,
                std::vector<Ice::Byte>& outParams,
                const Ice::Context& context = Ice::noExplicitContext);
 
// array-mapping overload that maps the sequence<byte> input parameter to an array:
//
bool ice_invoke(const std::string& operation,
                Ice::OperationMode mode,
                const std::pair<const Ice::Byte*, const Ice::Byte*>& in,
                std::vector<Ice::Byte>& out,
                const Ice::Context& = Ice::noExplicitContext);
bool ice_invoke(const std::string& operation,
                Ice::OperationMode mode,
                const std::vector<Ice::Byte>& inParams,
                std::vector<Ice::Byte>& outParams,
                const Ice::Context& context = Ice::noExplicitContext);
 
// array-mapping overload that maps the sequence<byte> input parameter to an array:
//
bool ice_invoke(const std::string& operation,
                Ice::OperationMode mode,
                const std::pair<const Ice::Byte*, const Ice::Byte*>& in,
                std::vector<Ice::Byte>& out,
                const Ice::Context& = Ice::noExplicitContext);
namespace Ice
{
    public interface ObjectPrx
    {
        bool ice_invoke(string operation,
                        OperationMode mode,
                        byte[] inParams,
                        out byte[] outParams);
        // ...
    }
}

Another overload of ice_invoke (not shown) adds a trailing argument of type Ice.Context.

package com.zeroc.Ice;

public interface Object
{
    public class Ice_invokeResult
    {
        public Ice_invokeResult()
        {
        }

        public Ice_invokeResult(boolean returnValue, byte[] outParams)
        {
            this.returnValue = returnValue;
            this.outParams = outParams;
        }

        public boolean returnValue;
        public byte[] outParams;
    }

    ...
}

public interface ObjectPrx
{
    com.zeroc.Ice.Object.Ice_invokeResult ice_invoke(String operation, OperationMode mode, byte[] inParams);
 
    ...
}

Another overload of ice_invoke (not shown) adds a trailing argument of type Ice.Context.

boolean ice_invoke(String operation,
                   Ice.OperationMode mode,
                   byte[] inParams,
                   Ice.ByteSeqHolder outParams);

Another overload of ice_invoke (not shown) adds a trailing argument of type Ice.Context.

-(BOOL) ice_invoke:(NSString*)operation mode:(ICEOperationMode)mode inEncaps:(NSData*)inEncaps outEncaps:(NSMutableData**)outEncaps;

Another overload of ice_invoke (not shown) adds a trailing argument of type Ice.Context.

def ice_invoke(self, operation, mode, inParams, context=None)

Upon completion, the method returns a tuple consisting of (ok, outParams), where ok is True if the invocation completed successfully and outParams contains the encapsulated output parameters, or False if the invocation resulted in a user exception and outParams contains the encapsulated exception.

Calling ice_invoke

The code below demonstrates how to invoke the operation op, which takes no in parameters:

std::shared_ptr<Ice::ObjectPrx> proxy = ...
try
{
    std::vector<Ice::Byte> inParams, outParams;
    if(proxy->ice_invoke("op", Ice::OperationMode::Normal, inParams, outParams)) 
    {
        // Handle success
    } 
    else 
    {
        // Handle user exception
    }
} 
catch(const Ice::LocalException& ex)
{
    // Handle exception
}

As a convenience, the Ice run time accepts an empty byte sequence when there are no input parameters and internally translates it into an empty encapsulation. In all other cases, the value for inParams must be an encapsulation of the encoded parameters.

Ice::ObjectPrx proxy = ...
try
{
    std::vector<Ice::Byte> inParams, outParams;
    if(proxy->ice_invoke("op", Ice::Normal, inParams, outParams)) 
    {
        // Handle success
    } 
    else 
    {
        // Handle user exception
    }
} 
catch(const Ice::LocalException& ex)
{
    // Handle exception
}

As a convenience, the Ice run time accepts an empty byte sequence when there are no input parameters and internally translates it into an empty encapsulation. In all other cases, the value for inParams must be an encapsulation of the encoded parameters.

Ice.ObjectPrx proxy = ...
try
{
    byte[] outParams;
    if(proxy.ice_invoke("op", Ice.OperationMode.Normal, null, out outParams))
    {
        // Handle success
    }
    else
    {
        // Handle user exception
    }
}
catch (Ice.LocalException ex)
{
    // Handle exception
}

As a convenience, the Ice run time accepts an empty or null byte sequence when there are no input parameters and internally translates it into an empty encapsulation. In all other cases, the value for inParams must be an encapsulation of the encoded parameters.

com.zeroc.Ice.ObjectPrx proxy = ...
try
{
    com.zeroc.Ice.Object.Ice_invokeResult r = proxy.ice_invoke("op", com.zeroc.Ice.OperationMode.Normal, null);
    if(r.returnValue)
    {
        // Handle success
    }
    else
    {
        // Handle user exception
    }
}
catch(com.zeroc.Ice.LocalException ex)
{
    // Handle exception
}

As a convenience, the Ice run time accepts an empty or null byte sequence when there are no input parameters and internally translates it into an empty encapsulation. In all other cases, the value for inParams must be an encapsulation of the encoded parameters.

Ice.ObjectPrx proxy = ...
try
{
    Ice.ByteSeqHolder outParams = new Ice.ByteSeqHolder();
    if(proxy.ice_invoke("op", Ice.OperationMode.Normal, null, outParams))
    {
        // Handle success
    }
    else
    {
        // Handle user exception
    }
}
catch(Ice.LocalException ex)
{
    // Handle exception
}

As a convenience, the Ice run time accepts an empty or null byte sequence when there are no input parameters and internally translates it into an empty encapsulation. In all other cases, the value for inParams must be an encapsulation of the encoded parameters.

proxy = ...
try:
    [ok, outParams] = proxy.ice_invoke("op", Ice.OperationMode.Normal, None)
    if ok:
        # Handle success
    else:
        # Handle user exception
except Ice.LocalException as ex:
    # Handle exception

Asynchronous Mapping for ice_invoke

The asynchronous mapping for ice_invoke resembles the static AMI mapping. The return value and the parameters operationmode, and inParams have the same semantics as for the synchronous version of ice_invoke shown above.

Proxy member functions
// "future" async function
// with P = std::promise (the default), it returns a std::future<Ice::Object::Ice_invokeResult>
//
template<template<typename> class P = std::promise> auto
ice_invokeAsync(const std::string& operation,
                Ice::OperationMode mode,
                const std::vector<Byte>& inP,
                const ::Ice::Context& context = Ice::noExplicitContext)
        -> decltype(std::declval<P<Ice::Object::Ice_invokeResult>>().get_future());
 
// async function with callbacks
//
std::function<void()>
ice_invokeAsync(const std::string& operation,
                Ice::OperationMode mode,
                const std::vector<Ice::Byte>& inP,
                std::function<void(bool, const std::vector<Ice::Byte>&)> response,
                std::function<void(std::exception_ptr)> ex = nullptr,
                std::function<void(bool)> sent = nullptr,
                const Ice::Context& context = Ice::noExplicitContext);
 
// "future" async function, with "zero-copy" in parameter
// with P = std::promise (the default), it returns a std::future<Ice::Object::Ice_invokeResult>
//
template<template<typename> class P = std::promise> auto
ice_invokeAsync(const std::string& operation,
                Ice::OperationMode mode,
                const std::pair<const Ice::Byte*, const Ice::Byte*>& inP,
                const Ice::Context& context = Ice::noExplicitContext)
        -> decltype(std::declval<P<Ice::Object::Ice_invokeResult>>().get_future());


// async function with callbacks, and "zero-copy" in parameter
//
std::function<void()>
ice_invokeAsync(const std::string& operation,
                Ice::OperationMode mode,
                const std::pair<const Ice::Byte*, const Ice::Byte*>& inP,
                std::function<void(bool, const std::pair<const Ice::Byte*, const Ice::Byte*>&)> response,
                std::function<void(std::exception_ptr)> ex = nullptr,
                std::function<void(bool)> sent = nullptr,
                const Ice::Context& context = Ice::noExplicitContext);
Ice::Object
class Object
{
public:
    ...
    struct Ice_invokeResult
    {
        bool returnValue;
        std::vector<Ice::Byte> outParams;
    };
};

ice_invokeAsync with Oneway Proxy

You can call ice_invokeAsync on a oneway proxy, provided the operation has a void return type, does not have any out-parameter, and does not raise any user exception. If you call ice_invokeAsync on a oneway proxy for an operation that returns values or raises a user exception, you will get a TwowayOnlyException.

With the callback API, ice_invokeAsync on a oneway proxy does not call the supplied response callback; you can use the sent callback to make sure the invocation is sent successfullyWith the future-based API, the returned future is a future<void> and this future is made ready when the invocation is sent.

Proxy member functions
// Basic (no callback)
//
Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::vector<Ice::Byte>& inParams);

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::vector<Ice::Byte>& inParams,
                                     const Ice::Context& context);

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::pair<const Ice::Byte*, const Ice::Byte*>& inParams);

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::pair<const Ice::Byte*, const Ice::Byte*>& inParams,
                                     const Ice::Context& context,
                                     const Ice::LocalObjectPtr& cookie = 0);
 
bool end_ice_invoke(std::vector<Ice::Byte>&, const Ice::AsyncResultPtr&);
 
// With generic callback - use Ice::newCallback to create these generic callbacks
//
Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::vector<Ice::Byte>& inParams,
                                     const:Ice::CallbackPtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::vector<Ice::Byte>& inParams,
                                     const Ice::Context& context,
                                     const Ice::CallbackPtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::pair<const Ice::Byte*, const Ice::Byte*>& inParams,
                                     const Ice::CallbackPtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);
   

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::pair<const Ice::Byte*, const Ice::Byte*>& inParams,
                                     const Ice::Context& context,
                                     const Ice::CallbackPtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);

// With type-safe callback - use newCallback_Object_ice_invoke (below) to create these callbacks
//
Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::vector<Ice::Byte>& inParams,
                                     const Ice::Callback_Object_ice_invokePtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::vector<Ice::Byte>& inParams,
                                     const Ice::Context& context,
                                     const Ice::Callback_Object_ice_invokePtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);

Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::pair<const Ice::Byte*, const Ice::Byte*>& inParams,
                                     const Ice::Callback_Object_ice_invokePtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);
    
Ice::AsyncResultPtr begin_ice_invoke(const std::string& op,
                                     Ice::OperationMode mode,
                                     const std::pair<const Ice::Byte*, const Ice::Byte*>& inParams,
                                     const Ice::Context& context,
                                     const Ice::Callback_Object_ice_invokePtr& del,
                                     const Ice::LocalObjectPtr& cookie = 0);
 
// factory functions for type-safe callbacks
// Versions are provided to support zero-copy semantics for the byte sequence containing the operation's results, 
// as well as a cookie value whose type is inferred and represented here by the CT (cookie type) symbol
//
template<class T> Callback_Object_ice_invokePtr
newCallback_Object_ice_invoke(const IceUtil::Handle<T>& instance,
                              void (T::*cb)(bool, const std::vector<Ice::Byte>&),
                              void (T::*excb)(const Ice::Exception&),
                              void (T::*sentcb)(bool) = 0);

template<class T> Callback_Object_ice_invokePtr
newCallback_Object_ice_invoke(const IceUtil::Handle<T>& instance,
                              void (T::*cb)(bool, const std::pair<const Ice::Byte*, const Ice::Byte*>&),
                              void (T::*excb)(const Ice::Exception&),
                              void (T::*sentcb)(bool) = 0);

template<class T, typename CT> Callback_Object_ice_invokePtr
newCallback_Object_ice_invoke(const IceUtil::Handle<T>& instance,
                              void (T::*cb)(bool, const std::vector<Ice::Byte>&, const CT&),
                              void (T::*excb)(const Ice::Exception&, const CT&),
                              void (T::*sentcb)(bool, const CT&) = 0);

template<class T, typename CT> Callback_Object_ice_invokePtr
newCallback_Object_ice_invoke(const IceUtil::Handle<T>& instance,
                              void (T::*cb)(bool, const std::pair<const Ice::Byte*, const Ice::Byte*>&,
                                            const CT&),
                              void (T::*excb)(const Ice::Exception&, const CT&),
                              void (T::*sentcb)(bool, const CT&) = 0);

template<class T> Callback_Object_ice_invokePtr
newCallback_Object_ice_invoke(const IceUtil::Handle<T>& instance,
                              void (T::*excb)(const Ice::Exception&),
                              void (T::*sentcb)(bool) = 0);

template<class T, typename CT> Callback_Object_ice_invokePtr
newCallback_Object_ice_invoke(const IceUtil::Handle<T>& instance,
                              void (T::*excb)(const Ice::Exception&, const CT&),
                              void (T::*sentcb)(bool, const CT&) = 0);
 

The AsyncResult API is deprecated and provided only for backward compatibility. New applications should use the Task API.

The basic mapping is shown below:

Ice.AsyncResult<Ice.Callback_Object_ice_invoke>
begin_ice_invoke(
    string operation,
    Ice.OperationMode mode,
    byte[] inParams);

Ice.AsyncResult<Callback_Object_ice_invoke>
begin_ice_invoke(
    string operation,
    Ice.OperationMode mode,
    byte[] inParams,
    Dictionary<string, string> context);

bool end_ice_invoke(out byte[] outParams, AsyncResult r);

User exceptions are handled differently than for static asynchronous invocations. Calling end_ice_invoke can raise Ice run-time exceptions but never raises user exceptions. Instead, the boolean return value of end_ice_invoke indicates whether the operation completed successfully (true) or raised a user exception (false). If the return value is true, the byte sequence contains an encapsulation of the results; otherwise, the byte sequence contains an encapsulation of the user exception.

The generic callback API is also available:

Ice.AsyncResult begin_ice_invoke(
    string operation,
    Ice.OperationMode mode,
    byte[] inParams,
    Ice.AsyncCallback cb,
    object cookie);

Ice.AsyncResult begin_ice_invoke(
    string operation,
    Ice.OperationMode mode,
    byte[] inParams,
    Dictionary<string, string> context,
    Ice.AsyncCallback cb,
    object cookie);

Refer to the static AMI mapping for a callback example.

For the type-safe callback API, you register callbacks on the AsyncResult object just as in the static AMI mapping:

public class MyCallback
{
    public void responseCB(bool ret, byte[] results)
    {
        if(ret)
        {
            System.Console.Out.WriteLine("Success");
        }
        else
        {
            System.Console.Out.WriteLine("User exception");
        }
    }

    public void failureCB(Ice.Exception ex)
    {
        System.Console.Err.WriteLine("Exception is: " + ex);
    }
}

...

Ice.AsyncResult<Ice.Callback_Object_ice_invoke> r = proxy.begin_ice_invoke(...);
MyCallback cb = new MyCallback();
r.whenCompleted(cb.responseCB, cb.failureCB);

The caller invokes whenCompleted on the AsyncResult object and supplies delegates to handle response and failure. The response delegate must match the signature of Ice.Callback_Object_ice_invoke:

public delegate void Callback_Object_ice_invoke(bool ret, byte[] outParams);
System.Threading.Tasks.Task<Ice.Object_Ice_invokeResult>
ice_invokeAsync(string operation,
                Ice.OperationMode mode,
                byte[] inParams,
                Ice.OptionalContext context = new Ice.OptionalContext(),
                System.IProgress<bool> progress = null,
                System.Threading.CancellationToken cancel = new System.Threading.CancellationToken());

The method sends (or queues) an invocation of the given operation and does not block the calling thread. It returns a Task that you can use in a number of ways, including blocking to obtain the result, configuring a continuation to be executed when the result becomes available, and polling to check the status of the request. Refer to the static AMI mapping for more information on the contextprogress and cancel arguments.

The ice_invokeAsync signature is consistent with the AMI mapping of operations that return multiple values, therefore it uses a structure as its result type:

namespace Ice
{
    public struct Object_Ice_invokeResult
    {
        public Object_Ice_invokeResult(bool returnValue, byte[] outEncaps);
        public bool returnValue;
        public byte[] outEncaps;
    }
}

User exceptions are handled differently than for static asynchronous invocations. Calling ice_invokeAsync can raise Ice run-time exceptions but never raises user exceptions. Instead, the returnValue member of Object_Ice_invokeResult indicates whether the operation completed successfully (true) or raised a user exception (false). If returnValue is true, the byte sequence in outEncaps contains an encapsulation of the results; otherwise, the byte sequence in outEncaps contains an encapsulation of the user exception.

package com.zeroc.Ice;
 
public interface Object
{
    public class Ice_invokeResult
    {
        public Ice_invokeResult()
        {
        }

        public Ice_invokeResult(boolean returnValue, byte[] outParams)
        {
            this.returnValue = returnValue;
            this.outParams = outParams;
        }

        public boolean returnValue;
        public byte[] outParams;
    }

    ...
}
 
public interface ObjectPrx
{
    java.util.concurrent.CompletableFuture<com.zeroc.Ice.Object.Ice_invokeResult> ice_invokeAsync(
        String operation,
        OperationMode mode,
        byte[] inParams);

    java.util.concurrent.CompletableFuture<com.zeroc.Ice.Object.Ice_invokeResult> ice_invokeAsync(
        String operation,
        OperationMode mode,
        byte[] inParams,
        java.util.Map<String, String> context);
 
    ...
}

As for statically-typed asynchronous invocations, the return value is a CompletableFuture. Its result is an instance of Object.Ice_invokeResult. Run-time exceptions cause the future to fail exceptionally, however user exceptions cause the future to succeed: the returnValue member of the Ice_invokeResult object will be set to false, and the outParams member contains the encapsulated user exception data.

Ice.AsyncResult begin_ice_invoke(
    String operation,
    Ice.OperationMode mode,
    byte[] inParams);

Ice.AsyncResult begin_ice_invoke(
    String operation,
    Ice.OperationMode mode,
    byte[] inParams,
    java.util.Map<String, String> __context);

boolean end_ice_invoke(Ice.ByteSeqHolder outParams, Ice.AsyncResult __result);

User exceptions are handled differently than for static asynchronous invocations. Calling end_ice_invoke can raise run-time exceptions but never raises user exceptions. Instead, the boolean return value of end_ice_invoke indicates whether the operation completed successfully (true) or raised a user exception (false). If the return value is true, the byte sequence contains an encapsulation of the results; otherwise, the byte sequence contains an encapsulation of the user exception.

The generic callback API is also available:

Ice.AsyncResult begin_ice_invoke(
    String operation,
    Ice.OperationMode mode,
    byte[] inParams,
    Ice.Callback cb);

Ice.AsyncResult begin_ice_invoke(
    String operation,
    Ice.OperationMode mode,
    byte[] inParams,
    java.util.Map<String, String> context,
    Ice.Callback cb);

Refer to the static AMI mapping for an example of subclassing Ice.Callback.

The type-safe callback API looks as follows:

Ice.AsyncResult begin_ice_invoke(
    String operation,
    Ice.OperationMode mode,
    byte[] inParams,
    Ice.Callback_Object_ice_invoke cb);

Ice.AsyncResult begin_ice_invoke(
    String operation,
    Ice.OperationMode mode,
    byte[] inParams,
    java.util.Map<String, String> context,
    Ice.Callback_Object_ice_invoke cb);

Callers must supply a subclass of Ice.Callback_Object_ice_invoke:

package Ice;

public abstract class Callback_Object_ice_invoke extends ...
{
    public abstract void response(boolean ret, byte[] outParams);

    public abstract void exception(LocalException ex);
}

The boolean argument to response indicates whether the operation completed successfully (true) or raised a user exception (false). If the return value is true, the byte sequence contains an encapsulation of the results; otherwise, the byte sequence contains an encapsulation of the user exception.

-(id<ICEAsyncResult>) begin_ice_invoke:(NSString*)operation mode:(ICEOperationMode)mode inEncaps:(NSData*)inEncaps;
-(id<ICEAsyncResult>) begin_ice_invoke:(NSString*)operation mode:(ICEOperationMode)mode inEncaps:(NSData*)inEncaps
                                        response:(void(^)(BOOL, NSMutableData*))response exception:(void(^)(ICEException*))exception;
-(id<ICEAsyncResult>) begin_ice_invoke:(NSString*)operation mode:(ICEOperationMode)mode inEncaps:(NSData*)inEncaps
                                        response:(void(^)(BOOL, NSMutableData*))response exception:(void(^)(ICEException*))exception
                                        sent:(void(^)(BOOL))sent;
-(BOOL) end_ice_invoke:(NSMutableData**)outEncaps result:(id<ICEAsyncResult>)result;

The begin_ice_invoke methods send (or queue) an invocation of the given operation and do not block the calling thread. Refer to the static AMI mapping for more information on the responseexception and sent arguments.

def ice_invokeAsync(self, operation, mode, inParams, context=None)

The method sends (or queues) an invocation of the given operation and does not block the calling thread. It returns a Future that you can use in a number of ways, including blocking to obtain the result, configuring a callback to be executed when the result becomes available, and polling to check the status of the request. Refer to the static AMI mapping for more information on using futures.

Upon completion, the result of the future is a tuple consisting of (ok, outParams), where ok is True if the invocation completed successfully and outParams contains the encapsulated output parameters, or False if the invocation resulted in a user exception and outParams contains the encapsulated exception.


Using Streams with ice_invoke

The streaming interfaces provide the tools an application needs to dynamically invoke operations with arguments of any Slice type. Consider the following Slice definition:

Slice
module Calc 
{
    exception Overflow
    {
        int x;
        int y;
    }

    interface Compute
    {
        idempotent int add(int x, int y) throws Overflow;
    }
}

Now let's write a client that dynamically invokes the add operation:

std::shared_ptr<Ice::ObjectPrx> proxy = ...
try
{
    std::vector<Ice::Byte> inParams, outParams;

    Ice::OutputStream out(communicator);
    out.startEncapsulation();
    int x = 100, y = -1;
    out.write(x);
    out.write(y);
    out.endEncapsulation();
    out.finished(inParams);

    if(proxy->ice_invoke("add", Ice::OperationMode::Idempotent, inParams, outParams))
    {
        // Handle success
        InputStream in(communicator, outParams);
        in.startEncapsulation();
        int result;
        in.read(result);
        in.endEncapsulation();
        assert(result == 99);
    } 
    else 
    {
        // Handle user exception
    }
} 
catch(const Ice::LocalException& ex)
{
    // Handle exception
}
Ice::ObjectPrx proxy = ...try
{
    std::vector<Ice::Byte> inParams, outParams;

    Ice::OutputStream out(communicator);
    out.startEncapsulation();
    int x = 100, y = -1;
    out.write(x);
    out.write(y);
    out.endEncapsulation();
    out.finished(inParams);

    if(proxy->ice_invoke("add", Ice::Idempotent, inParams, outParams))
    {
        // Handle success
        InputStream in(communicator, outParams);
        in.startEncapsulation();
        int result;
        in.read(result);
        in.endEncapsulation();
        assert(result == 99);
    } 
    else 
    {
        // Handle user exception
    }
} 
catch(const Ice::LocalException& ex)
{
    // Handle exception
}
Ice.ObjectPrx proxy = ...
try
{
    Ice.OutputStream outStream = new Ice.OutputStream(communicator);
    outStream.startEncapsulation();
    int x = 100, y = -1;
    outStream.writeInt(x);
    outStream.writeInt(y);
    outStream.endEncapsulation();
    byte[] inParams = outStream.finished();
 
    byte[] outParams;
    if(proxy.ice_invoke("add", Ice.OperationMode.Idempotent, inParams, out outParams))
    {
        // Handle success
        Ice.InputStream inStream = new Ice.InputStream(communicator, outParams);
        inStream.startEncapsulation();
        int result = inStream.readInt();
        inStream.endEncapsulation();
        System.Diagnostics.Debug.Assert(result == 99);
    }
    else
    {
        // Handle user exception
    }
}
catch (Ice.LocalException ex)
{
    // Handle exception
}
com.zeroc.Ice.ObjectPrx proxy = ...
try
{
    com.zeroc.Ice.OutputStream out = new com.zeroc.Ice.OutputStream(communicator);
    out.startEncapsulation();
    int x = 100, y = -1;
    out.writeInt(x);
    out.writeInt(y);
    out.endEncapsulation();
    byte[] inParams = out.finished();
 
    com.zeroc.Ice.Object.Ice_invokeResult r = proxy.ice_invoke("add", com.zeroc.Ice.OperationMode.Idempotent, inParams);
    if(r.returnValue)
    {
        // Handle success
        com.zeroc.Ice.InputStream in = new com.zeroc.Ice.InputStream(communicator, r.outParams);
        in.startEncapsulation();
        int result = in.readInt();
        in.endEncapsulation();
        assert(result == 99);
    }
    else
    {
        // Handle user exception
    }
}
catch(com.zeroc.Ice.LocalException ex)
{
    // Handle exception
}
Ice.ObjectPrx proxy = ...
try
{
    Ice.OutputStream out = new Ice.OutputStream(communicator);
    out.startEncapsulation();
    int x = 100, y = -1;
    out.writeInt(x);
    out.writeInt(y);
    out.endEncapsulation();
    byte[] inParams = out.finished();
 
    Ice.ByteSeqHolder outParams = new Ice.ByteSeqHolder();
    if(proxy.ice_invoke("add", Ice.OperationMode.Idempotent, inParams, outParams))
    {
        // Handle success
        Ice.InputStream in = new Ice.InputStream(communicator, outParams.value);
        in.startEncapsulation();
        int result = in.readInt();
        in.endEncapsulation();
        assert(result == 99);
    }
    else
    {
        // Handle user exception
    }
}
catch(Ice.LocalException ex)
{
    // Handle exception
}

You can see here that the input and output parameters are enclosed in encapsulations.

We neglected to handle the case of a user exception in this example, so let's implement that now. We assume that we have compiled our program with the Slice-generated code, therefore we can call throwException on the input stream and catch Overflow directly:

if(proxy->ice_invoke("add", Ice::OperationMode::Idempotent, inParams, outParams))
{
    // Handle success
    // ...
}
else
{
    // Handle user exception
    Ice::InputStream in(communicator, outParams);
    try
    {
        in.startEncapsulation();
        in.throwException();
    }
    catch(const Calc::Overflow& ex)
    {
        cout << "overflow while adding " << ex.x << " and " << ex.y << endl;
    } 
    catch(const Ice::UserException& ex)
    {
        // Handle unexpected user exception
    }
}
if(proxy->ice_invoke("add", Ice::Idempotent, inParams, outParams))
{
    // Handle success
    // ...
}
else
{
    // Handle user exception
    Ice::InputStream in(communicator, outParams);
    try
    {
        in.startEncapsulation();
        in.throwException();
    }
    catch(const Calc::Overflow& ex)
    {
        cout << "overflow while adding " << ex.x << " and " << ex.y << endl;
    } 
    catch(const Ice::UserException& ex)
    {
        // Handle unexpected user exception
    }
}
    if(proxy.ice_invoke("add", Ice.OperationMode.Idempotent, inParams, out outParams))
    {
        // Handle success
        ...
    }
    else
    {
        // Handle user exception
        Ice.InputStream inStream = new Ice.InputStream(communicator, outParams);
        try
        {
            inStream.startEncapsulation();
            inStream.throwException();
        }
        catch(Calc.Overflow ex)
        {
            System.Console.WriteLine("overflow while adding " +
                                     ex.x + " and " + ex.y);
        }
        catch(Ice.UserException)
        {
            // Handle unexpected user exception
        }
    }
    if(r.returnValue)
    {
        // Handle success
        // ...
    }
    else
    {
        // Handle user exception
        com.zeroc.Ice.InputStream in = new com.zeroc.Ice.InputStream(communicator, r.outParams);
        try
        {
            in.startEncapsulation();
            in.throwException();
        }
        catch(Calc.Overflow ex)
        {
            System.out.println("overflow while adding " + ex.x + " and " + ex.y);
        }
        catch(com.zeroc.Ice.UserException ex)
        {
            // Handle unexpected user exception
        }
    }
    if(proxy.ice_invoke("add", Ice.OperationMode.Idempotent, inParams, outParams))
    {
        // Handle success
        // ...
    }
    else
    {
        // Handle user exception
        Ice.InputStream in = new Ice.InputStream(communicator, outParams.value);
        try
        {
            in.startEncapsulation();
            in.throwException();
        }
        catch(Calc.Overflow ex)
        {
            System.out.println("overflow while adding " + ex.x + " and " + ex.y);
        }
        catch(Ice.UserException ex)
        {
            // Handle unexpected user exception
        }
    }


This is obviously a contrived example: if the Slice-generated code is available, why bother using dynamic dispatch? In the absence of Slice-generated code, the caller would need to manually unmarshal the user exception, which is outside the scope of this manual.

As a defensive measure, the code traps UserException. This could be raised if the Slice definition of add is modified to include another user exception but this segment of code did not get updated accordingly.

See Also