Table of Contents Previous Next
Logo
Dynamic Ice : 35.2 Streaming Interface
Copyright © 2003-2010 ZeroC, Inc.

35.2 Streaming Interface

Ice provides a convenient interface for streaming Slice types to and from a sequence of bytes. You can use this interface in many situations, such as when serializing types for persistent storage, and when using Ice’s dynamic invocation and dispatch interfaces (see Section 35.3).
The streaming interface is not defined in Slice, but rather is a collection of native classes provided by each language mapping1. A default implementation of the interface uses the Ice encoding as specified in Section 37.2, but other imple­mentations are possible.
There are two primary abstract classes in the streaming interface: InputStream and OutputStream. As you might guess, InputStream is used to extract Slice types from a sequence of bytes, while OutputStream is used to convert Slice types into a sequence of bytes. The classes provide the func­tions necessary to manipulate all of the core Slice types:
• Primitives (bool, int, string, etc.)
• Sequences of primitives
• Proxies
• Objects
The classes also provide functions that handle various details of the Ice encoding. Using these functions, you can manually insert and extract constructed types, such as dictionaries and structures, but doing so is tedious and error-prone. To make insertion and extraction of constructed types easier, the Slice compilers can optionally generate helper functions that manage the low-level details for you.
The remainder of this section describes the streaming interface for each supported language mapping. To properly use the streaming interface, you should be familiar with the Ice encoding (see Section 37.2). An example that demon­strates the use of the streaming interface is located in demo/Ice/invoke in the Ice distribution.

35.2.1 C++ Stream Interface

We discuss the stream classes first, followed by the helper functions, and finish with an advanced use case of the streaming interface.

InputStream

An InputStream is created using the following function:
namespace Ice {
    InputStreamPtr createInputStream(
        const Ice::CommunicatorPtr& communicator,
        const std::vector<Ice::Byte>& data);
}
The InputStream class is shown below.
namespace Ice {
    class InputStream : ... {
    public:
        virtual CommunicatorPtr communicator() const = 0;

        virtual void sliceObjects(bool slice) = 0;

        virtual void read(bool& v) = 0;
        virtual void read(Byte& v) = 0;
        virtual void read(Short& v) = 0;
        virtual void read(Int& v) = 0;
        virtual void read(Long& v) = 0;
        virtual void read(Float& v) = 0;
        virtual void read(Double& v) = 0;
        virtual void read(std::string& s,
                          bool convert = true) = 0;
        virtual void read(std::wstring& s) = 0;

        template<typename T> inline void read(T& v) {
            StreamReader< StreamTrait<T>::type>::read(this, v);
        }

        virtual void read(std::vector<std::string>& v,
                          bool convert) = 0;

        virtual void read(
                        std::pair<const bool*, const bool*>&,
                        IceUtil::ScopedArray<bool>&) = 0;

        virtual void read(
                        std::pair<const Byte*, const Byte*>&) = 0;

        virtual void read(std::pair<const Short*, const Short*>&,
                        IceUtil::ScopedArray<Short>&) = 0;

        virtual void read(
                        std::pair<const Int*, const Int*>&,
                        IceUtil::ScopedArray<Int>&) = 0;

        virtual void read(
                        std::pair<const Long*, const Long*>&,
                        IceUtil::ScopedArray<Long>&) = 0;

        virtual void read(
                        std::pair<const Float*, const Float*>&,
                        IceUtil::ScopedArray<Float>&) = 0;

        virtual void read(
                        std::pair<const Double*, const Double*>&,
                        IceUtil::ScopedArray<Double>&) = 0;

        virtual Int readSize() = 0;
        virtual Int readAndCheckSeqSize(int minWireSize) = 0;

        virtual ObjectPrx readProxy() = 0;

        template<typename T> inline void
        read(IceInternal::ProxyHandle<T>& v) {
            // ...
        }

        virtual void readObject(
                        const ReadObjectCallbackPtr& cb) = 0;

        template<typename T> inline void
        read(IceInternal::Handle<T>& v) {
            // ...
        }

        virtual std::string readTypeId() = 0;

        virtual void throwException() = 0;

        virtual void startSlice() = 0;
        virtual void endSlice() = 0;
        virtual void skipSlice() = 0;

        virtual void startEncapsulation() = 0;
        virtual void endEncapsulation() = 0;
        virtual void skipEncapsulation() = 0;

        virtual void readPendingObjects() = 0;

        virtual void rewind() = 0;
    };
    typedef ... InputStreamPtr;
}

Extracting Built-In Types

Member functions are provided to extract any of the built-in types. For example, you can extract a double value followed by a string from a stream as follows:
vector<Ice::Byte> data = ...;
in = Ice::createInputStream(communicator, data);
double d;
in>read(d);
string s;
in>read(s);

Extracting Sequences of Built-In Type

For other types, the following template member function performs the extraction:
template<typename T> inline void
read(T& v) {
    StreamReader<StreamTrait<T>::type>::read(this, v);
}
For example, you can extract a sequence of integers as follows:
vector<Ice::Byte> data = ...;
in = Ice::createInputStream(communicator, data);
// ...
IntSeq s; // Slice: sequence<int> IntSeq;
in>read(s);
The Ice run time provides an implementation of the StreamReader template whose read method reads a sequence of any of the built-in types. Note that, when reading a sequence, this reads both the sequence size that precedes the sequence elements as well as the sequence elements that follow the size.
If you are using a custom container for your sequence of built-in type, you must provide a specialization of the StreamTrait template in order to extract your sequence. For example, the following definition allows you to use the QVector container from the Qt library:
//
// StreamTrait specialization for QVector
//
template<typename T>
struct StreamTrait< QVector<T> >
{
    static const StreamTraitType type = StreamTraitTypeSequence;
    static const int minWireSize = 1;
};

Extracting Sequences of Built-In Type Using Zero-Copy

Note that InputStream provides a number of overloads that accept a pair of pointers. For example, you can extract a sequence of bytes as follows:
vector<Ice::Byte> data = ...;
in = Ice::createInputStream(communicator, data);
std::pair<const Ice::Byte*, const Ice::Byte*> p;
in>read(p);
The same extraction technique works for the other built-in integral and floating-point types, such int and double.
If the extraction is for a byte sequence, the returned pointers always point at memory in the stream’s internal marshaling buffer.
For the other built-in types, the pointers refer to the internal marshaling buffer only if the Ice encoding is compatible with the machine and compiler representa­tion of the type, otherwise the pointers refer to a temporary array allocated to hold the unmarshaled data. The overloads for zero-copy extraction accept an additional parameter of type IceUtil::ScopedArray (see Appendix F) that holds this temporary array when necessary.
Here is an example to illustrate how to extract a sequence of integers, regard­less of whether the machine’s encoding of integers matches the on-the-wire repre­sentation or not:
#include <IceUtil/ScopedArray.h>
...
in = Ice::createInputStream(communicator, data);
std::pair<const Ice::Int*, const Ice::Int*> p;
IceUtil::ScopedArray<Ice::Int> a;
in>read(p, a);

for(const Ice::Int* i = p.first; i != p.second; ++i) {
    cout << *i << endl;
}
If the on-the-wire encoding matches that of the machine, and therefore zero-copy is possible, the returned pair of pointers points into the run time’s internal marshaling buffer. Otherwise, the run time allocates an array, unmarshals the data into the array, and sets the pair of pointers to point into that array. Use of the ScopedArray helper template ensures that the array is deallocated once you let the ScopedArray go out of scope, so there is no need to call delete[]. (ScopedArray is conceptually the same as the Ptr smart pointer types for classes. See Appendix F for details.)

Extracting Structures

Without the stream option to slice2cpp, you must extract structures member by member according to the rules in Chapter 37. Otherwise, with stream, slice2cpp generates code that allows you to extract the structure directly. For example, here is how you can extract a Slice structure called MyStruct from a stream:
in = Ice::createInputStream(communicator, data);
MyStruct myStruct;
in>read(myStruct);

Extracting Dictionaries

Without the stream option to slice2cpp, you can extract any dictionary whose key and value types are built-in types; for any other dictionary, you must extract it as a size followed by its entries according to the rules in Chapter 37. If you are using a custom container for your dictionary of built-in types, you must provide a specialization of the StreamTrait template in order to extract your dictionary. For example, the following definition allows you to use the QMap container from the Qt library:
//
// StreamTrait specialization for QMap
//
template<typename K, typename V>
struct StreamTrait< QMap<K, V> >
{
    static const StreamTraitType type = StreamTraitTypeDictionary;
    static const int minWireSize = 1;
};
With the stream option, slice2cpp generates code that allows you to extract any dictionary directly, for example:
in = Ice::createInputStream(communicator, data);
MyDict myDict; // Slice: dictionary<string, SomeType> MyDict;
in>read(myDict);

Extracting Sequences of User-Defined Type

Without the stream option to slice2cpp, you must extract sequences of user-defined type as a size followed by the element type according to the rules in Chapter 37. Otherwise, with stream, slice2cpp generates code that allows you to extract a sequence directly, for example:
in = Ice::createInputStream(communicator, data);
MyEnumS myEnumS; // Slice: sequence<MyEnum> myEnumS;
in>read(myEnumS);

Other Member Functions

The remaining member functions of InputStream have the following seman­tics:
• void sliceObjects(bool slice)
Determines the behavior of the stream when extracting Ice objects. An Ice object is “sliced” when a factory cannot be found for a Slice type id (see Section 37.2.11 for more information), resulting in the creation of an object of a less-derived type. Slicing is typically disabled when the application expects all object factories to be present, in which case the exception NoObject­FactoryException is raised. The default behavior is to allow slicing.
• void read(std::string& v, bool convert = true)
void read(std::vector<std::string>& v,
          bool convert = true)
The optional boolean argument determines whether the strings unmarshaled by these methods are processed by the string converter, if one is installed. The default behavior is to convert the strings. See Section 32.24 for more informa­tion on string converters.
• Ice::Int readSize()
The Ice encoding has a compact representation to indicate size (see Section 37.2.1). This function extracts a size and returns it as an integer.
• Ice::Int readAndCheckSeqSize(int minWireSize)
Like readSize, this function reads a size and returns it, but also verifies that there is enough data remaining in the unmarshaling buffer to successfully unmarshal the elements of the sequence. The minWireSize parameter indi­cates the smallest possible on-the-wire representation of a single sequence element (see Chapter 37). If the unmarshaling buffer contains insufficient data to unmarshal the sequence, the function throws UnmarshalOutOfBound­sException.
• Ice::ObjectPrx readProxy()
This function returns an instance of the base proxy type, ObjectPrx.
• template<typename T> inline void
read(IceInternal::ProxyHandle<T>& v)
This template function behaves like readProxy but avoids the need to down-cast the return value. You can pass a proxy of any type as the parameter v.
• void readObject(const Ice::ReadObjectCallbackPtr &)
The Ice encoding for class instances requires extraction to occur in stages (see Section 37.2.11). The readObject function accepts a callback object of type ReadObjectCallback, whose definition is shown below:
namespace Ice {
    class ReadObjectCallback : ... {
    public:
        virtual void invoke(const Ice::ObjectPtr&) = 0;
    };
    typedef ... ReadObjectCallbackPtr;
}
When the object instance is available, the callback object's invoke member function is called. The application must call readPendingObjects to ensure that all instances are properly extracted.
If you are not interested in receiving a callback when the object is extracted, it is easier to use the read(IceInternal::Handle<T>&) template func­tion instead (see below).
• template<typename T> inline void
read(IceInternal::Handle<T>& v)
This template function behaves like readObject but avoids the need to supply a callback. You can pass a smart pointer of any type as the parameter v. Note that, if you want to intercept object extraction, you must use readObject instead.
• std::string readTypeId()
A table of Slice type ids is used to save space when encoding Ice objects (see Section 37.2.11). This function returns the type id at the stream’s current posi­tion.
• void throwException()
This function extracts a user exception from the stream and throws it. If the stored exception is of an unknown type, the function attempts to extract and throw a less-derived exception. If that also fails, an UnmarshalOutOfBoundsException is thrown.
• void startSlice()
void endSlice()
void skipSlice()
Start, end, and skip a slice of member data, respectively. These functions are used when manually extracting the slices of an Ice object or user exception. See Section 37.2.11 for more information.
• void startEncapsulation()
void endEncapsulation()
void skipEncapsulation()
Start, end, and skip an encapsulation, respectively. See Section 37.2.2 for more information.
• void readPendingObjects()
An application must call this function after all other data has been extracted, but only if Ice objects were encoded. This function extracts the state of Ice objects and invokes their corresponding callback objects (see readObject).
• void rewind()
Resets the position of the stream to the beginning.

OutputStream

An OutputStream is created using the following function:
namespace Ice {
    OutputStreamPtr createOutputStream(
        const Ice::CommunicatorPtr& communicator);
}
The OutputStream class is shown below.
namespace Ice {
    class OutputStream : ... {
    public:
        virtual Ice::CommunicatorPtr communicator() const = 0;

        virtual void write(bool v) = 0;
        virtual void write(Byte v) = 0;
        virtual void write(Short v) = 0;
        virtual void write(Int v) = 0;
        virtual void write(Long v) = 0;
        virtual void write(Float v) = 0;
        virtual void write(Double v) = 0;
        virtual void write(const std::string& v,
                           bool convert = true) = 0;
        virtual void write(const char* v,
                           bool convert = true) = 0;
        virtual void write(const std::wstring& v) = 0;

        virtual void write(const bool* begin,
                           const bool* end) = 0;
        virtual void write(const Byte* begin,
                           const Byte* end) = 0;
        virtual void write(const Short* begin,
                           const Short* end) = 0;
        virtual void write(const Int* begin,
                           const Int* end) = 0;
        virtual void write(const Long* begin,
                           const Long* end) = 0;
        virtual void write(const Float* begin,
                           const Float* end) = 0;
        virtual void write(const Double* begin,
                           const Double* end) = 0;    

        virtual void write(const std::vector<std::string>& v,
                           bool convert) = 0;

        template<typename T> inline void
        write(const T& v) {
            StreamWriter<StreamTrait<T>::type>::write(this, v);
        }

        virtual void writeSize(Ice::Int sz) = 0;

        virtual void writeProxy(const Ice::ObjectPrx& v) = 0;

        template<typename T> inline void
        write(const IceInternal::ProxyHandle<T>& v) {
            // ...
        }

        virtual void writeObject(const Ice::ObjectPtr& v) = 0;

        template<typename T> inline void
        write(const IceInternal::Handle<T>& v) {
            // ...
        }

        virtual void writeTypeId(const std::string& id) = 0;

        virtual void writeException(
                        const Ice::UserException& e) = 0;

        virtual void startSlice() = 0;
        virtual void endSlice() = 0;

        virtual void startEncapsulation() = 0;
        virtual void endEncapsulation() = 0;

        virtual void writePendingObjects() = 0;

        virtual void finished(std::vector<Ice::Byte>& v) = 0;

        virtual void reset(bool) = 0;
    };
}

Inserting Built-In Types

Member functions are provided to insert any of the built-in types. For example, you can insert a double value followed by a string into a stream as follows:
out = Ice::createOutputStream(communicator);
Ice::Double d = 3.14;
out>write(d);
string s = "Hello";
out>write(s);

Inserting Sequences of Built-In Type

For other types, the following template member function performs the insertion:
template<typename T> inline void
write(const T& v) {
    StreamWriter<StreamTrait<T>::type>::write(this, v);
}
For example, you can insert a sequence of integers as follows:
out = Ice::createOutputStream(communicator);
IntSeq s = ...;
out>write(s);
The Ice run time provides an implementation of the StreamWriter template whose write method writes a sequence of any of the built-in types. Note that, when writing a sequence, this writes both the sequence size that precedes the sequence elements and the sequence elements that follow the size.
If you are using a custom container for your sequence of built-in type, you must provide a specialization of the StreamTrait template in order to insert your sequence. For example, the following definition allows you to use the QVector container from the Qt library:
//
// StreamTrait specialization for QVector
//
template<typename T>
struct StreamTrait< QVector<T> >
{
    static const StreamTraitType type = StreamTraitTypeSequence;
    static const int minWireSize = 1;
};

Inserting Sequences of Built-In Type Using Zero-Copy

Note that OutputStream provides a number of overloads that accept a pair of pointers. For example, you can insert a sequence of bytes as follows:
out = Ice::createOutputStream(communicator);
vector<Ice::Byte> data = ...;
out>write(&v[0], &v[v.size()]);
The same insertion technique works for the other built-in integral and floating-point types, such int and double. Insertion in this way can avoid an additional data copy during marshaling if the internal representation of the data in memory is the same as the on-the-wire representation. (Note that the two pointers must point at a contiguous block of memory.)

Inserting Structures

Without the stream option to slice2cpp, you must insert structures member by member according to the rules in Chapter 37. Otherwise, with stream, slice2cpp generates code that allows you to insert the structure directly. For example, here is how you can insert a Slice structure called MyStruct into a stream:
out = Ice::createOutputStream(communicator);
MyStruct myStruct;
// Initialize myStruct...
out>write(myStruct);

Inserting Dictionaries

Without the stream option to slice2cpp, you can insert any dictionary whose key and value types are built-in types; for any other dictionary, you must insert it as a size followed by its entries according to the rules in Chapter 37. If you are using a custom container for your dictionary of built-in types, you must provide a specialization of the StreamTrait template in order to insert your dictionary. For example, the following definition allows you to use the QMap container from the Qt library:
//
// StreamTrait specialization for QMap
//
template<typename K, typename V>
struct StreamTrait< QMap<K, V> >
{
    static const StreamTraitType type = StreamTraitTypeDictionary;
    static const int minWireSize = 1;
};
With the stream option, slice2cpp generates code that allows you to insert any dictionary directly, for example:
out = Ice::createOutputStream(communicator);
MyDict myDict; // Slice: dictionary<int, SomeType> MyDict;
// Initialize myDict...
out>write(myDict);

Inserting Sequences of User-Defined Type

Without the stream option to slice2cpp, you must insert sequences of user-defined type as a size followed by the element type according to the rules in Chapter 37. Otherwise, with stream, slice2cpp generates code that allows you to insert a sequence directly, for example:
out = Ice::createOutputStream(communicator);
MyEnumS myEnumS; // Slice: sequence<MyEnum> myEnumS;
// Initialize myEnumS...
out>write(myEnumS);

Other Member Functions

The remaining member functions of OutputStream have the following seman­tics:
• void write(const std::string& v,
           bool convert = true)
void write(const char* v, bool convert = true)
void write(const std::vector<std::string>&,
           bool convert = true)
The optional boolean argument determines whether the strings marshaled by these methods are processed by the string converter, if one is installed. The default behavior is to convert the strings. See Section 32.24 for more informa­tion on string converters.
• void writeSize(Ice::Int sz)
The Ice encoding has a compact representation to indicate size (see Section 37.2.1). This function converts the given non-negative integer into the proper encoded representation.
• void writeProxy(const Ice::ObjectPtr & v)
Inserts a proxy.
• template<typename T> inline void
write(const IceInternal::ProxyHandle<T>& v)
This template function behaves like writeProxy. You can pass a proxy of any type as the parameter v.
• void writeObject(const Ice::ObjectPtr & v)
Inserts an Ice object. The Ice encoding for class instances (see Section 37.2.11) may cause the insertion of this object to be delayed, in which case the stream retains a reference to the given object and the stream does not insert its state it until writePendingObjects is invoked on the stream.
• template<typename T> inline void
write(const IceInternal::Handle<T>& v)
This template function behaves like writeObject. You can pass a smart pointer of any type as the parameter v.
• void writeTypeId(const std::string & id)
A table of Slice type ids is used to save space when encoding Ice objects (see Section 37.2.11). This function adds the given type id to the table and encodes the type id. writeTypeId may only be invoked in the context of a call to writePendingObjects (see below).
• void writeException(const Ice::UserException & ex)
Inserts a user exception. You can also use the template member function write(const T&) to insert a user exception.
• void startSlice()
void endSlice()
Starts and ends a slice of object or exception member data (see Section 37.2.11).
• void startEncapsulation()
void endEncapsulation()
Starts and ends an encapsulation, respectively (see Section 37.2.2).
• void writePendingObjects()
Encodes the state of Ice objects whose insertion was delayed during writ­eObject. This member function must only be called once.
• void finished(std::vector< Ice::Byte > & data)
Indicates that marshaling is complete. The given byte sequence is filled with the encoded data. This member function must only be called once.
• void reset(bool clearBuffer)
Resets the writing position of the stream to the beginning. If clearBuffer is true, the stream releases the memory it has allocated to hold the encoded data.

Intercepting Object Insertion and Extraction

In some situations it may be necessary to intercept the insertion and extraction of Ice objects. For example, the Ice extension for PHP (see Chapter 28) is imple­mented using Ice for C++ but represents Ice objects as native PHP objects. The PHP extension accomplishes this by manually encoding and decoding Ice objects as directed by Section 37.2. However, the extension obviously cannot pass a native PHP object to the C++ stream function writeObject. To bridge this gap between object systems, Ice supplies the classes ObjectReader and ObjectWriter:
namespace Ice {
    class ObjectReader : public Ice::Object {
    public:
        virtual void read(const InputStreamPtr&, bool) = 0;
        // ...
    };
    typedef ... ObjectReaderPtr;

    class ObjectWriter : public Ice::Object {
    public:
        virtual void write(const OutputStreamPtr&) const = 0;
        // ...
    };
    typedef ... ObjectWriterPtr;
}
A foreign Ice object is inserted into a stream using the following technique:
1. A C++ “wrapper” class is derived from ObjectWriter. This class wraps the foreign object and implements the write member function.
2. An instance of the wrapper class is passed to writeObject. (This is possible because ObjectWriter derives from Ice::Object.) Eventu­ally, the write member function is invoked on the wrapper instance.
3. The implementation of write encodes the object’s state as directed by Section 37.2.11.
It is the application’s responsibility to ensure that there is a one-to-one mapping between foreign Ice objects and wrapper objects. This is necessary in order to ensure the proper encoding of object graphs.
Extracting the state of a foreign Ice object is more complicated than insertion:
1. A C++ “wrapper” class is derived from ObjectReader. An instance of this class represents a foreign Ice object.
2. An object factory is installed that returns instances of the wrapper class. Note that a single object factory can be used for all Slice types if it is registered with an empty Slice type id (see Section 6.14.5).
3. A C++ callback class is derived from ReadObjectCallback. The imple­mentation of invoke expects its argument to be either nil or an instance of the wrapper class as returned by the object factory.
4. An instance of the callback class is passed to readObject.
5. When the stream is ready to extract the state of an object, it invokes read on the wrapper class. The implementation of read decodes the object’s state as directed by Section 37.2.11. The boolean argument to read indicates whether the function should invoke readTypeId on the stream; it is possible that the type id of the current slice has already been read, in which case this argument is false.
6. The callback object passed to readObject is invoked, passing the instance of the wrapper object. All other callback objects representing the same instance in the stream (in case of object graphs) are invoked with the same wrapper object.

Intercepting User Exception Insertion

Similar to the discussion of Ice objects in the previous section, a Dynamic Ice application may represent user exceptions in a native format that is not directly compatible with the Ice API. If the application needs to raise such a user exception to the Ice run time, the exception must be wrapped in a subclass of Ice::UserException. The Dynamic Ice API provides a class to simplify this process:
namespace Ice {
    class UserExceptionWriter : public UserException {
    public:
        UserExceptionWriter(const CommunicatorPtr&);

        virtual void write(const OutputStreamPtr&) const = 0;
        virtual bool usesClasses() const = 0;

        virtual std::string ice_name() const = 0;
        virtual Ice::Exception* ice_clone() const = 0;
        virtual void ice_throw() const = 0;

        // ...
    };
    typedef ... UserExceptionWriterPtr;
}
A subclass of UserExceptionWriter is responsible for supplying a commu­nicator to the constructor, and for implementing the following methods:
• void write(const OutputStreamPtr&) const
This method is invoked when the Ice run time is ready to marshal the excep­tion. The subclass must marshal the exception using the encoding rules speci­fied in Section 37.2.10.
• bool usesClasses() const
Return true if the exception, or any base exception, contains a data member for an object by value.
• std::string ice_name() const
Return the Slice name of the exception.
• Ice::Exception* ice_clone() const
Return a copy of the exception.
• void ice_throw() const
Raise the exception by calling throw *this.

35.2.2 Java Stream Interface

We discuss the stream classes first, followed by the helper functions, and finish with an advanced use case of the streaming interface.

InputStream

An InputStream is created using the following function:
package Ice;

public class Util {
    public static InputStream
    createInputStream(Communicator communicator, byte[] data);
}
The InputStream interface is shown below.
package Ice;

public interface InputStream {
    Communicator communicator();

    void sliceObjects(boolean slice);

    boolean readBool();
    boolean[] readBoolSeq();

    byte readByte();
    byte[] readByteSeq();

    short readShort();
    short[] readShortSeq();

    int readInt();
    int[] readIntSeq();

    long readLong();
    long[] readLongSeq();

    float readFloat();
    float[] readFloatSeq();

    double readDouble();
    double[] readDoubleSeq();

    String readString();
    String[] readStringSeq();

    int readSize();
    int readAndCheckSeqSize(int minSizeWireSize);

    ObjectPrx readProxy();

    void readObject(ReadObjectCallback cb);

    String readTypeId();

    void throwException() throws UserException;

    void startSlice();
    void endSlice();
    void skipSlice();

    void startEncapsulation();
    void endEncapsulation();
    void skipEncapsulation();

    void readPendingObjects();

    java.io.Serializable readSerializable();

    void rewind()_;

    void destroy();
}
Member functions are provided for extracting all of the primitive types, as well as sequences of primitive types; these are self-explanatory. The remaining member functions have the following semantics:
• void sliceObjects(boolean slice)
Determines the behavior of the stream when extracting Ice objects. An Ice object is “sliced” when a factory cannot be found for a Slice type id (see Section 37.2.11 for more information), resulting in the creation of an object of a less-derived type. Slicing is typically disabled when the application expects all object factories to be present, in which case the exception NoObject­FactoryException is raised. The default behavior is to allow slicing.
• int readSize()
The Ice encoding has a compact representation to indicate size (see Section 37.2.1). This function extracts a size and returns it as an integer.
• int readAndCheckSeqSize(int minWireSize)
Like readSize, this function reads a size and returns it, but also verifies that there is enough data remaining in the unmarshaling buffer to successfully unmarshal the elements of the sequence. The minWireSize parameter indi­cates the smallest possible on-the-wire representation of a single sequence element (see Chapter 37). If the unmarshaling buffer contains insufficient data to unmarshal the sequence, the function throws UnmarshalOutOfBound­sException.
• Ice.ObjectPrx readProxy()
This function returns an instance of the base proxy type, ObjectPrx. The Slice compiler optionally generates helper functions to extract proxies of user-defined types (see page 1220).
• void readObject(ReadObjectCallback cb)
The Ice encoding for class instances requires extraction to occur in stages (see Section 37.2.11). The readObject function accepts a callback object of type ReadObjectCallback, whose definition is shown below:
package Ice;

public interface ReadObjectCallback {
    void invoke(Ice.Object obj);
}
When the object instance is available, the callback object's invoke member function is called. The application must call readPendingObjects to ensure that all instances are properly extracted.
Note that applications rarely need to invoke this member function directly; the helper functions generated by the Slice compiler are easier to use (see page 1230).
• String readTypeId()
A table of Slice type ids is used to save space when encoding Ice objects (see Section 37.2.11). This function returns the type id at the stream’s current posi­tion.
• void throwException() throws UserException
This function extracts a user exception from the stream and throws it. If the stored exception is of an unknown type, the function attempts to extract and throw a less-derived exception. If that also fails, an UnmarshalOutOfBoundsException is thrown.
• void startSlice()
void endSlice()
void skipSlice()
Start, end, and skip a slice of member data, respectively. These functions are used when manually extracting the slices of an Ice object or user exception. See Section 37.2.11 for more information.
• void startEncapsulation()
void endEncapsulation()
void skipEncapsulation()
Start, end, and skip an encapsulation, respectively. See Section 37.2.2 for more information.
• void readPendingObjects()
An application must call this function after all other data has been extracted, but only if Ice objects were encoded. This function extracts the state of Ice objects and invokes their corresponding callback objects (see readObject).
• java.io.Serializable readSerializable()
Reads a serializable Java object from the stream (see Section 10.15).
• void destroy()
Applications must call this function in order to reclaim resources.
Here is a simple example that demonstrates how to extract a boolean and a sequence of strings from a stream:
byte[] data = ...
Ice.InputStream in =
    Ice.Util.createInputStream(communicator, data);
try {
    boolean b = in.readBool();
    String[] seq = in.readStringSeq();
} finally {
    in.destroy();
}

OutputStream

An OutputStream is created using the following function:
package Ice;

public class Util {
    public static OutputStream createOutputStream(
        Communicator communicator);
}
The OutputStream class is shown below.
package Ice;

public interface OutputStream {
    Communicator communicator();

    void writeBool(boolean v);
    void writeBoolSeq(boolean[] v);

    void writeByte(byte v);
    void writeByteSeq(byte[] v);

    void writeShort(short v);
    void writeShortSeq(short[] v);

    void writeInt(int v);
    void writeIntSeq(int[] v);

    void writeLong(long v);
    void writeLongSeq(long[] v);

    void writeFloat(float v);
    void writeFloatSeq(float[] v);

    void writeDouble(double v);
    void writeDoubleSeq(double[] v);

    void writeString(String v);
    void writeStringSeq(String[] v);

    void writeSize(int sz);

    void writeProxy(ObjectPrx v);

    void writeObject(Ice.Object v);

    void writeTypeId(String id);

    void writeException(UserException ex);

    void startSlice();
    void endSlice();

    void startEncapsulation();
    void endEncapsulation();

    void writePendingObjects();

    byte[] finished();

    void reset(boolean clearBuffer);

    void writeSerializable(java.io.Serializable o);

    void destroy();
}
Member functions are provided for inserting all of the primitive types, as well as sequences of primitive types; these are self-explanatory. The remaining member functions have the following semantics:
• void writeSize(int sz)
The Ice encoding has a compact representation to indicate size (see Section 37.2.1). This function converts the given non-negative integer into the proper encoded representation.
• void writeProxy(Ice.ObjectPrx v)
Inserts a proxy.
• void writeObject(Ice.Object v)
Inserts an Ice object. The Ice encoding for class instances (see Section 37.2.11) may cause the insertion of this object to be delayed, in which case the stream retains a reference to the given object and does not insert its state it until writePendingObjects is invoked on the stream.
• void writeTypeId(String id)
A table of Slice type ids is used to save space when encoding Ice objects (see Section 37.2.11). This function adds the given type id to the table and encodes the type id. writeTypeId may only be invoked in the context of a call to writePendingObjects (see below).
• void writeException(UserException ex)
Inserts a user exception.
• void startSlice()
void endSlice()
Starts and ends a slice of object or exception member data (see Section 37.2.11).
• void startEncapsulation()
void endEncapsulation()
Starts and ends an encapsulation, respectively (see Section 37.2.2).
• void writePendingObjects()
Encodes the state of Ice objects whose insertion was delayed during writ­eObject. This member function must only be called once.
• byte[] finished()
Indicates that marshaling is complete and returns the encoded byte sequence. This member function must only be called once.
• void reset(boolean clearBuffer)
Resets the writing position of the stream to the beginning. The boolean argu­ment clearBuffer determines whether the stream releases the internal buffer it allocated to hold the encoded data. If clearBuffer is true, the stream releases the buffer in order to make it eligible for garbage collection. If clearBuffer is false, the stream retains the buffer to avoid generating unnecessary garbage.
• void writeSerializable(java.io.Serializable v)
Writes a serializable Java object to the stream (see Section 10.15).
• void destroy()
Applications must call this function in order to reclaim resources.
Here is a simple example that demonstrates how to insert a boolean and a sequence of strings into a stream:
final String[] seq = { "Ice", "rocks!" };
Ice.OutputStream out = Ice.Util.createOutputStream(communicator);
try {
    out.writeBool(true);
    out.writeStringSeq(seq);
    byte[] data = out.finished();
} finally {
    out.destroy();
}

Helper Functions

The stream classes provide all of the low-level functions necessary for encoding and decoding Ice types. However, it would be tedious and error-prone to manually encode complex Ice types such as classes, structs, and dictionaries using these low-level functions. For this reason, the Slice compiler (see Section 10.18.1) optionally generates helper functions for streaming complex Ice types.
We will use the following Slice definitions to demonstrate the language mapping:
module M {
    sequence<...> Seq;
    dictionary<...> Dict;
    struct S {
        ...
    };
    enum E { ... };
    class C {
        ...
    };
};
The Slice compiler generates the corresponding helper functions shown below:
package M;

public class SeqHelper {
    public static T[] read(Ice.InputStream in);
    public static void write(Ice.OutputStream out, T[] v);
}

public class DictHelper {
    public static java.util.Map read(Ice.InputStream in);
    public static void write(Ice.OutputStream out,
                             java.util.Map v);
}

public class SHelper {
    public static S read(Ice.InputStream in);
    public static void write(Ice.OutputStream out, S v);
}

public class EHelper {
    public static E read(Ice.InputStream in);
    public static void write(Ice.OutputStream out, E v);
}

public class CHelper {
    public static void read(Ice.InputStream in, CHolder h);
    public static void write(Ice.OutputStream out, C v);
}

public class CPrxHelper {
    public static CPrx read(Ice.InputStream in);
    public static void write(Ice.OutputStream out, CPrx v);
}
In addition, the Slice compiler generates the following member functions for struct and enum types:
public class S ... {
    ...
    public void ice_read(Ice.InputStream in);
    public void ice_write(Ice.OutputStream out);
};
public class E... {
    ...
    public void ice_read(Ice.InputStream in);
    public void ice_write(Ice.OutputStream out);
}
Be aware that a call to CHelper.read does not result in the immediate extrac­tion of an Ice object. The value member of the given CHolder object is updated when readPendingObjects is invoked on the input stream.

Intercepting Object Insertion and Extraction

In some situations it may be necessary to intercept the insertion and extraction of Ice objects. For example, the Ice extension for PHP (see Chapter 28) is imple­mented using Ice for C++ but represents Ice objects as native PHP objects. The PHP extension accomplishes this by manually encoding and decoding Ice objects as directed by Section 37.2. However, the extension obviously cannot pass a native PHP object to the C++ stream function writeObject. To bridge this gap between object systems, Ice supplies the classes ObjectReader and ObjectWriter:
package Ice;

public abstract class ObjectReader extends ObjectImpl {
    public abstract void read(InputStream in, boolean rid);
    // ...
}

public abstract class ObjectWriter extends ObjectImpl {
    public abstract void write(OutputStream out);
    // ...
}
A foreign Ice object is inserted into a stream using the following technique:
1. A Java “wrapper” class is derived from ObjectWriter. This class wraps the foreign object and implements the write member function.
2. An instance of the wrapper class is passed to writeObject. (This is possible because ObjectWriter derives from Ice.Object.) Eventually, the write member function is invoked on the wrapper instance.
3. The implementation of write encodes the object’s state as directed by Section 37.2.11.
It is the application’s responsibility to ensure that there is a one-to-one mapping between foreign Ice objects and wrapper objects. This is necessary in order to ensure the proper encoding of object graphs.
Extracting the state of a foreign Ice object is more complicated than insertion:
1. A Java “wrapper” class is derived from ObjectReader. An instance of this class represents a foreign Ice object.
2. An object factory is installed that returns instances of the wrapper class. Note that a single object factory can be used for all Slice types if it is registered with an empty Slice type id (see Section 10.14.4).
3. A Java callback class implements the ReadObjectCallback interface. The implementation of invoke expects its argument to be either null or an instance of the wrapper class as returned by the object factory.
4. An instance of the callback class is passed to readObject.
5. When the stream is ready to extract the state of an object, it invokes read on the wrapper class. The implementation of read decodes the object’s state as directed by Section 37.2.11. The boolean argument to read indicates whether the function should invoke readTypeId on the stream; it is possible that the type id of the current slice has already been read, in which case this argument is false.
6. The callback object passed to readObject is invoked, passing the instance of the wrapper object. All other callback objects representing the same instance in the stream (in case of object graphs) are invoked with the same wrapper object.

Intercepting User Exception Insertion

Similar to the discussion of Ice objects in the previous section, a Dynamic Ice application may represent user exceptions in a native format that is not directly compatible with the Ice API. If the application needs to raise such a user exception to the Ice run time, the exception must be wrapped in a subclass of Ice::UserException. The Dynamic Ice API provides a class to simplify this process:
package Ice;

public abstract class UserExceptionWriter extends UserException {

    public UserExceptionWriter(Communicator communicator);

    public abstract void write(Ice.OutputStream os);
    public abstract boolean usesClasses();

    // ...
}
A subclass of UserExceptionWriter is responsible for supplying a commu­nicator to the constructor, and for implementing the following methods:
• void write(OutputStream os)
This method is invoked when the Ice run time is ready to marshal the excep­tion. The subclass must marshal the exception using the encoding rules speci­fied in Section 37.2.10.
• boolean usesClasses()
Return true if the exception, or any base exception, contains a data member for an object by value.

35.2.3 C# Stream Interface

We discuss the stream classes first, followed by the helper functions, and finish with an advanced use case of the streaming interface.

InputStream

An InputStream is created using the following function:
namespace Ice
{
    public sealed class Util
    {
        public static InputStream createInputStream(
                                    Communicator communicator,
                                    byte[] bytes);
    }
}
The InputStream interface is shown below.
namespace Ice
{
    public interface InputStream
    {
        Communicator communicator();

        void sliceObjects(bool slice);

        bool readBool();
        bool[] readBoolSeq();

        byte readByte();
        byte[] readByteSeq();

        short readShort();
        short[] readShortSeq();

        int readInt();
        int[] readIntSeq();

        long readLong();
        long[] readLongSeq();

        float readFloat();
        float[] readFloatSeq();

        double readDouble();
        double[] readDoubleSeq();

        string readString();
        string[] readStringSeq();

        int readSize();
        int readAndCheckSeqSize(int minSize);

        ObjectPrx readProxy();

        void readObject(ReadObjectCallback cb);

        string readTypeId();

        void throwException();

        void startSlice();
        void endSlice();
        void skipSlice();

        void startEncapsulation();
        void endEncapsulation();
        void skipEncapsulation();
        int getEncapsulationSize();

        void readPendingObjects();

        object readSerializable();

        void rewind();

        void destroy();
    }
}
Member functions are provided for extracting all of the primitive types, as well as sequences of primitive types; these are self-explanatory. The remaining member functions have the following semantics:
• void sliceObjects(boolean slice)
Determines the behavior of the stream when extracting Ice objects. An Ice object is “sliced” when a factory cannot be found for a Slice type id (see Section 37.2.11 for more information), resulting in the creation of an object of a less-derived type. Slicing is typically disabled when the application expects all object factories to be present, in which case the exception NoObject­FactoryException is raised. The default behavior is to allow slicing.
• int readSize()
The Ice encoding has a compact representation to indicate size (see Section 37.2.1). This function extracts a size and returns it as an integer.
• int readAndCheckSeqSize(int minWireSize)
Like readSize, this function reads a size and returns it, but also verifies that there is enough data remaining in the unmarshaling buffer to successfully unmarshal the elements of the sequence. The minWireSize parameter indi­cates the smallest possible on-the-wire representation of a single sequence element (see Chapter 37). If the unmarshaling buffer contains insufficient data to unmarshal the sequence, the function throws UnmarshalOutOfBound­sException.
• Ice.ObjectPrx readProxy()
This function returns an instance of the base proxy type, ObjectPrx. The Slice compiler optionally generates helper functions to extract proxies of user-defined types (see page 1241).
• void readObject(ReadObjectCallback cb)
The Ice encoding for class instances requires extraction to occur in stages (see Section 37.2.11). The readObject function accepts a callback object of type ReadObjectCallback, whose definition is shown below:
namespace Ice
{
    public interface ReadObjectCallback
    {
        void invoke(Ice.Object obj);
    }
}
When the object instance is available, the callback object's invoke member function is called. The application must call readPendingObjects to ensure that all instances are properly extracted.
Note that applications rarely need to invoke this member function directly; the helper functions generated by the Slice compiler are easier to use (see page 1241).
• string readTypeId()
A table of Slice type ids is used to save space when encoding Ice objects (see Section 37.2.11). This function returns the type id at the stream’s current posi­tion.
• void throwException()
This function extracts a user exception from the stream and throws it. If the stored exception is of an unknown type, the function attempts to extract and throw a less-derived exception. If that also fails, an UnmarshalOutOfBoundsException is thrown.
• void startSlice()
void endSlice()
void skipSlice()
Start, end, and skip a slice of member data, respectively. These functions are used when manually extracting the slices of an Ice object or user exception. See Section 37.2.11 for more information.
• void startEncapsulation()
void endEncapsulation()
void skipEncapsulation()
Start, end, and skip an encapsulation, respectively. See Section 37.2.2 for more information.
• int getEncapsulationSize()
Returns the size of the current encapsulation in bytes.
• void readPendingObjects()
An application must call this function after all other data has been extracted, but only if Ice objects were encoded. This function extracts the state of Ice objects and invokes their corresponding callback objects (see readObject).
• object readSerializable()
Reads a serializable .NET object from the stream (see Section 14.14).
• void rewind()
Resets the position of the stream to the beginning.
• void destroy()
Applications must call this function in order to reclaim resources.
Here is a simple example that demonstrates how to extract a boolean and a sequence of strings from a stream:
byte[] data = ...
Ice.InputStream inStream =
    Ice.Util.createInputStream(communicator, data);
try {
    bool b = inStream.readBool();
    string[] seq = inStream.readStringSeq();
} finally {
    inStream.destroy();
}

OutputStream

An OutputStream is created using the following function:
namespace Ice
{
    public sealed class Util
    {
        public static OutputStream createOutputStream(
                                    Communicator communicator);
    }
}
The OutputStream class is shown below.
namespace Ice
{
    public interface OutputStream
    {
        Communicator communicator();

        void writeBool(bool v);
        void writeBoolSeq(bool[] v);

        void writeByte(byte v);
        void writeByteSeq(byte[] v);

        void writeShort(short v);
        void writeShortSeq(short[] v);

        void writeInt(int v);
        void writeIntSeq(int[] v);

        void writeLong(long v);
        void writeLongSeq(long[] v);

        void writeFloat(float v);
        void writeFloatSeq(float[] v);

        void writeDouble(double v);
        void writeDoubleSeq(double[] v);

        void writeString(string v);
        void writeStringSeq(string[] v);

        void writeSize(int sz);

        void writeProxy(ObjectPrx v);

        void writeObject(Ice.Object v);

        void writeTypeId(string id);

        void writeException(UserException ex);

        void startSlice();
        void endSlice();

        void startEncapsulation();
        void endEncapsulation();

        void writePendingObjects();

        byte[] finished();

        void reset(bool clearBuffer);

        void writeSerializable(object v);

        void destroy();
    }
}
Member functions are provided for inserting all of the primitive types, as well as sequences of primitive types; these are self-explanatory. The remaining member functions have the following semantics:
• void writeSize(int sz)
The Ice encoding has a compact representation to indicate size (see Section 37.2.1). This function converts the given non-negative integer into the proper encoded representation.
• void writeObject(Ice.Object v)
Inserts an Ice object. The Ice encoding for class instances (see Section 37.2.11) may cause the insertion of this object to be delayed, in which case the stream retains a reference to the given object and does not insert its state it until writePendingObjects is invoked on the stream.
• void writeProxy(Ice.ObjectPrx v)
Inserts a proxy.
• void writeTypeId(string id)
A table of Slice type ids is used to save space when encoding Ice objects (see Section 37.2.11). This function adds the given type id to the table and encodes the type id. writeTypeId may only be invoked in the context of a call to writePendingObjects (see below).
• void writeException(UserException ex)
Inserts a user exception.
• void startSlice()
void endSlice()
Starts and ends a slice of object or exception member data (see Section 37.2.11).
• void startEncapsulation()
void endEncapsulation()
Starts and ends an encapsulation, respectively (see Section 37.2.2).
• void writePendingObjects()
Encodes the state of Ice objects whose insertion was delayed during writ­eObject. This member function must only be called once.
• byte[] finished()
Indicates that marshaling is complete and returns the encoded byte sequence. This member function must only be called once.
• void reset(boolean clearBuffer)
Resets the writing position of the stream to the beginning. The boolean argu­ment clearBuffer determines whether the stream releases the internal buffer it allocated to hold the encoded data. If clearBuffer is true, the stream releases the buffer in order to make it eligible for garbage collection. If clearBuffer is false, the stream retains the buffer to avoid generating unnecessary garbage.
• void writeSerializable(object v)
Writes a serializable .NET object to the stream (see Section 14.14).
• void destroy()
Applications must call this function in order to reclaim resources.
Here is a simple example that demonstrates how to insert a boolean and a sequence of strings into a stream:
string[] seq = { "Ice", "rocks!" };
Ice.OutputStream outStream
    = Ice.Util.createOutputStream(communicator);
try {
    outStream.writeBool(true);
    outStream.writeStringSeq(seq);
    byte[] data = outStream.finished();
} finally {
    outStream.destroy();
}

Helper Functions

The stream classes provide all of the low-level functions necessary for encoding and decoding Ice types. However, it would be tedious and error-prone to manually encode complex Ice types such as classes, structs, and dictionaries using these low-level functions. For this reason, the Slice compiler (see Section 14.17) optionally generates helper functions for streaming complex Ice types.
We will use the following Slice definitions to demonstrate the language mapping:
module M {
    sequence<...> Seq;
    dictionary<...> Dict;
    struct S {
        ...
    };
    enum E { ... };
    class C {
        ...
    };
};
The Slice compiler generates the corresponding helper functions shown below:
namespace M
{
    public sealed class SeqHelper
    {
        public static int[] read(Ice.InputStream _in);
        public static void write(Ice.OutputStream _out, int[] _v);
    }

    public sealed class DictHelper
    {
        public static Dictionary<...> read(Ice.InputStream _in);
        public static void write(Ice.OutputStream _out,
                                 Dictionary<...> _v);
    }

    public sealed class SHelper
    {
        public static S read(Ice.InputStream _in);
        public static void write(Ice.OutputStream _out, S _v);
    }

    public sealed class EHelper
    {
        public static M.E read(Ice.InputStream _in);
        public static void write(Ice.OutputStream _out, M.E _v);
    }

    public sealed class CHelper
    {
        public CHelper(Ice.InputStream _in);
        public void read();
        public static void write(Ice.OutputStream _out, C _v);
        public M.C value
        {
            get;
        }
        // ...
    }

    public sealed class CPrxHelper : Ice.ObjectPrxHelperBase, CPrx
    {
        public static CPrx read(Ice.InputStream _in);
        public static void write(Ice.OutputStream _out, CPrx _v);
    }
}
In addition, the Slice compiler generates the following member functions for struct types:
public struct S {
    ...
    public void ice_read(Ice.InputStream in);
    public void ice_write(Ice.OutputStream out);
}
Be aware that a call to CHelper.read does not result in the immediate extrac­tion of an Ice object. The value property of the given CHelper object is updated when readPendingObjects is invoked on the input stream.

Intercepting Object Insertion and Extraction

In some situations it may be necessary to intercept the insertion and extraction of Ice objects. For example, the Ice extension for PHP (see Chapter 28) is imple­mented using Ice for C++ but represents Ice objects as native PHP objects. The PHP extension accomplishes this by manually encoding and decoding Ice objects as directed by Section 37.2. However, the extension obviously cannot pass a native PHP object to the C++ stream function writeObject. To bridge this gap between object systems, Ice supplies the classes ObjectReader and ObjectWriter:
namespace Ice
{
    public abstract class ObjectReader : ObjectImpl
    {
        public abstract void read(InputStream inStream, bool rid);
        // ...
    }

    public abstract class ObjectWriter : ObjectImpl
    {
        public abstract void write(OutputStream outStream);
        // ...
    }
}
A foreign Ice object is inserted into a stream using the following technique:
1. A C# “wrapper” class is derived from ObjectWriter. This class wraps the foreign object and implements the write member function.
2. An instance of the wrapper class is passed to writeObject. (This is possible because ObjectWriter derives from Ice.Object.) Eventually, the write member function is invoked on the wrapper instance.
3. The implementation of write encodes the object’s state as directed by Section 37.2.11.
It is the application’s responsibility to ensure that there is a one-to-one mapping between foreign Ice objects and wrapper objects. This is necessary in order to ensure the proper encoding of object graphs.
Extracting the state of a foreign Ice object is more complicated than insertion:
1. A C# “wrapper” class is derived from ObjectReader. An instance of this class represents a foreign Ice object.
2. An object factory is installed that returns instances of the wrapper class. Note that a single object factory can be used for all Slice types if it is registered with an empty Slice type id (see Section 10.14.4).
3. A C# callback class implements the ReadObjectCallback interface. The implementation of invoke expects its argument to be either null or an instance of the wrapper class as returned by the object factory.
4. An instance of the callback class is passed to readObject.
5. When the stream is ready to extract the state of an object, it invokes read on the wrapper class. The implementation of read decodes the object’s state as directed by Section 37.2.11. The boolean argument to read indicates whether the function should invoke readTypeId on the stream; it is possible that the type id of the current slice has already been read, in which case this argument is false.
6. The callback object passed to readObject is invoked, passing the instance of the wrapper object. All other callback objects representing the same instance in the stream (in case of object graphs) are invoked with the same wrapper object.

Intercepting User Exception Insertion

Similar to the discussion of Ice objects in the previous section, a Dynamic Ice application may represent user exceptions in a native format that is not directly compatible with the Ice API. If the application needs to raise such a user exception to the Ice run time, the exception must be wrapped in a subclass of Ice::UserException. The Dynamic Ice API provides a class to simplify this process:
namespace Ice
{
    public abstract class UserExceptionWriter : UserException
    {
        public UserExceptionWriter(Communicator communicator);

        public abstract void write(OutputStream os);
        public abstract bool usesClasses();

        // ...
    }
}
A subclass of UserExceptionWriter is responsible for supplying a commu­nicator to the constructor, and for implementing the following methods:
• void write(OutputStream os)
This method is invoked when the Ice run time is ready to marshal the excep­tion. The subclass must marshal the exception using the encoding rules speci­fied in Section 37.2.10.
• bool usesClasses()
Return true if the exception, or any base exception, contains a data member for an object by value.

1
The streaming interface is currently supported in C++, Java, and .NET.


Table of Contents Previous Next
Logo