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 operation
, mode
, and inParams
have the same semantics as for the synchronous version of ice_invoke
shown above.
// "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);
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 successfully. With the future-based API, the returned future is a future<void>
and this future is made ready when the invocation is sent.
// 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 context
, progress
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 response
, exception
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:
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.