This page describes the C++ mapping for the ice_invoke
proxy function and the Blobject
class.
On this page:
ice_invoke
in C++
The mapping for ice_invoke
is shown below:
bool ice_invoke( const std::string& operation, Ice::OperationMode mode, const std::vector< Ice::Byte >& inParams, std::vector< Ice::Byte >& outParams );
Another overloading of ice_invoke
(not shown) adds a trailing argument of type Ice::Context
.
As an example, the code below demonstrates how to invoke the operation op
, which takes no in
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 }
Using Streams with ice_invoke
in C++
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:
Ice::ObjectPrx proxy = ... try { std::vector< Ice::Byte > inParams, outParams; Ice::OutputStreamPtr out = Ice::createOutputStream(communicator); out->writeInt(100); // x out->writeInt(-1); // y out->finished(inParams); if (proxy->ice_invoke("add", Ice::Idempotent, inParams, outParams)) { // Handle success Ice::InputStreamPtr in = Ice::createInputStream(communicator, outParams); int result = in->readInt(); assert(result == 99); } else { // Handle user exception } } catch (const Ice::LocalException& ex) { // Handle exception }
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::Idempotent, inParams, outParams)) { // Handle success // ... } else { // Handle user exception Ice::InputStreamPtr in = Ice::createInputStream(communicator, outParams); try { 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 } }
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 Ice::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.
Subclassing Blobject
in C++
Implementing the dynamic dispatch model requires writing a subclass of Ice::Blobject
. We continue using the Compute
interface to demonstrate a Blobject
implementation:
class ComputeI : public Ice::Blobject { public: virtual bool ice_invoke( const std::vector<Ice::Byte>& inParams, std::vector<Ice::Byte>& outParams, const Ice::Current& current); };
An instance of ComputeI
is an Ice object because Blobject
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
For the purposes of this discussion, the implementation of ice_invoke
handles only the add
operation and raises OperationNotExistException
for all other operations. In a real implementation, the servant must also be prepared to receive invocations of the following Object
operations:
string ice_id()
Returns the Slice type ID of the servant's most-derived type.
StringSeq ice_ids()
Returns a sequence of strings representing all of the Slice interfaces supported by the servant, including"::Ice::Object"
.
bool ice_isA(string id)
Returnstrue
if the servant supports the interface denoted by the given Slice type ID, orfalse
otherwise. This operation is invoked by the proxy functioncheckedCast
.
void ice_ping()
Verifies that the object denoted by the identity and facet contained inIce::Current
is reachable.
With that in mind, here is our simplified version of ice_invoke
:
bool ComputeI::ice_invoke( const std::vector<Ice::Byte>& inParams, std::vector<Ice::Byte>& outParams, const Ice::Current& current) { if (current.operation == "add") { Ice::CommunicatorPtr communicator = current.adapter->getCommunicator(); Ice::InputStreamPtr in = Ice::createInputStream(communicator, inParams); int x = in->readInt(); int y = in->readInt(); Ice::OutputStreamPtr out = Ice::createOutputStream(communicator); if (checkOverflow(x, y)) { Calc::Overflow ex; ex.x = x; ex.y = y; out->writeException(ex); out->finished(outParams); return false; } else { out->writeInt(x + y); out->finished(outParams); return true; } } else { Ice::OperationNotExistException ex(__FILE__, __LINE__); ex.id = current.id; ex.facet = current.facet; ex.operation = current.operation; throw ex; } }
If an overflow is detected, the code "raises" the Calc::Overflow
user exception by calling writeException
on the output stream and returning false
, otherwise the return value is encoded and the function returns true
.
Using the Array Mapping for ice_invoke
and Blobject
in C++
Ice for C++ supports an alternative mapping for sequence
input parameters that avoids the overhead of extra copying. Since the ice_invoke
functions treat the encoded input parameters as a value of type sequence<byte>
, the dynamic invocation and dispatch facility includes interfaces that use the array mapping for the input parameter blob.
Ice provides two overloaded versions of the proxy function ice_invoke
that use the array mapping. The version that omits the trailing Ice::Context
argument is shown below:
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 );
A Blobject
servant uses the array mapping by deriving its implementation class from Ice::BlobjectArray
and overriding its ice_invoke
function:
class BlobjectArray { public: virtual bool ice_invoke( const std::pair<const Ice::Byte*, const Ice::Byte*>& in, std::vector<Ice::Byte>& out, const Ice::Current& current) = 0; };