The InputStream Interface in C++

On this page:

Initializing an InputStream in C++

The InputStream class provides a number of overloaded constructors:

namespace Ice
{
    class InputStream
    {
    public:
        InputStream();
        InputStream(const std::vector<Byte>&);
        InputStream(const std::pair<const Byte*, const Byte*>&);

        InputStream(const std::shared_ptr<Communicator>&);
        InputStream(const std::shared_ptr<Communicator>&, const std::vector<Byte>&);
        InputStream(const std::shared_ptr<Communicator>&, const std::pair<const Byte*, const Byte*>&);

        InputStream(const EncodingVersion&);
        InputStream(const EncodingVersion&, const std::vector<Byte>&);
        InputStream(const EncodingVersion&, const std::pair<const Byte*, const Byte*>&);

        InputStream(const std::shared_ptr<Communicator>&, const EncodingVersion&);
        InputStream(const std::shared_ptr<Communicator>&, const EncodingVersion&, const std::vector<Byte>&);
        InputStream(const std::shared_ptr<Communicator>&, const EncodingVersion&, const std::pair<const Byte*, const Byte*>&);

        ...
    };
} 
namespace Ice
{
    class InputStream
    {
    public:
        InputStream();
        InputStream(const std::vector<Byte>&);
        InputStream(const std::pair<const Byte*, const Byte*>&);

        InputStream(const CommunicatorPtr&);
        InputStream(const CommunicatorPtr&, const std::vector<Byte>&);
        InputStream(const CommunicatorPtr&, const std::pair<const Byte*, const Byte*>&);

        InputStream(const EncodingVersion&);
        InputStream(const EncodingVersion&, const std::vector<Byte>&);
        InputStream(const EncodingVersion&, const std::pair<const Byte*, const Byte*>&);

        InputStream(const CommunicatorPtr&, const EncodingVersion&);
        InputStream(const CommunicatorPtr&, const EncodingVersion&, const std::vector<Byte>&);
        InputStream(const CommunicatorPtr&, const EncodingVersion&, const std::pair<const Byte*, const Byte*>&);

        ...
    };
}

The constructors accept three types of arguments:

  • A communicator instance
  • An encoding version
  • The encoded data that you intend to decode

You'll normally supply the encoded data argument, which the stream accepts as either a vector or a pair of pointers to Ice::Byte.

For efficiency reasons, the InputStream does not copy the data argument - it only references it.

We recommend supplying a communicator instance, otherwise you will not be able to decode proxy objects. The stream also inspects the communicator's settings to configure several of its own default settings, but you can optionally configure these settings manually using functions that we'll describe later.

If you omit an encoding version, the stream uses the default encoding version of the communicator (if provided) or the most recent encoding version.

If a communicator instance is not available at the time you construct the stream, you can optionally supply it later using one of the overloaded initialize functions:

class InputStream
{
public:

   void initialize(const std::shared_ptr<Communicator>&);
   void initialize(const std::shared_ptr<Communicator>&, const EncodingVersion&);
   ...
};
class InputStream
{
public:
   void initialize(const CommunicatorPtr&);
   void initialize(const CommunicatorPtr&, const EncodingVersion&);
   ...
};

Invoking initialize causes the stream to re-initialize its settings based on the configuration of the given communicator.

Use the following functions to manually configure the stream:

namespace Ice
{
    class InputStream
    {
    public:
        void setValueFactoryManager(const std::shared_ptr<ValueFactoryManager>&);
        void setLogger(const std::shared_ptr<Logger>&);
        void setCompactIdResolver(std::function<std::string (int)>);
        void setSliceValues(bool);
        void setTraceSlicing(bool);

        ...
    };
}
C++98
namespace Ice
{
    class InputStream
    {
    public:
        void setValueFactoryManager(const ValueFactoryManagerPtr&);
        void setLogger(const LoggerPtr&);
        void setCompactIdResolver(const CompactIdResolverPtr&);
        void setCollectObjects(bool);
        void setSliceValues(bool);
        void setTraceSlicing(bool);

        ...
    };
}

The settings include:

  • Logger
    The stream uses a logger to record warning and trace messages.
  • Compact ID resolver
    A compact ID resolver for translating numeric values into Slice type IDs. The stream invokes the resolver by passing it the numeric compact ID. The resolver is expected to return the Slice type ID associated with that numeric ID or an empty string if the numeric ID is unknown. The C++11 API accepts a std::function whereas the C++98 API requires an instance of Ice::CompactIdResolver

    C++98
    class CompactIdResolver : public IceUtil::Shared 
    {
    public:
        virtual std::string resolve(Ice::Int) const = 0;
    };
    typedef IceUtil::Handle<CompactIdResolver> CompactIdResolverPtr;
  • Collectable
    The flag indicates whether to mark instances of Slice classes as collectable (C++98 only). If the stream is initialized with a communicator, this setting defaults to the value of the Ice.CollectObjects property, otherwise the setting defaults to false.
  • Slice values
    The flag indicates whether to slice instances of Slice classes to a known Slice type when a more derived type is unknown. An instance is "sliced" when no static information is available for a Slice type ID and no factory can be found for that type, resulting in the creation of an instance of a less-derived type. If slicing is disabled in this situation, the stream raises the exception NoValueFactoryException. The default behavior is to allow slicing.
  • Trace slicing
    The flag indicates whether to log messages when instances of Slice classes are sliced. If the stream is initialized with a communicator, this setting defaults to the value of the Ice.Trace.Slicing property, otherwise the setting defaults to false.

Extracting from an InputStream in C++

InputStream provides a number of overloaded read member functions that allow you to extract nearly all Slice types from the stream simply by calling read.

For example, you can extract a double value followed by a string from a stream as follows:

C++
vector<Ice::Byte> data = ...;
Ice::InputStream in(communicator, data);
double d;
in.read(d);
string s;
in.read(s);

Likewise, you can extract a sequence of a built-in type, or a complex type, from the stream as follows:

C++
vector<Ice::Byte> data = ...;
Ice::InputStream in(communicator, data);
// ...
IntSeq s; // Slice: sequence<int> IntSeq;
in.read(s);
 
ComplexType c;
in.read(c);

With the C++11 mapping, you can also call readAll with all the parameters to extract, for example the sample above can be rewritten as:

C++11
vector<Ice::Byte> data = ...;
Ice::InputStream in(communicator, data);
// ...
IntSeq s; // Slice: sequence<int> IntSeq; 
ComplexType c;
in.readAll(s, c);

Here are most of the functions for extracting data from a stream:

class InputStream
{
public:

    void read(bool&);
    void read(Byte&);
    void read(short&);
    void read(int&);
    void read(long long int&);
    void read(float&);
    void read(double&);

    void read(std::string& s, bool convert = true);
    void read(std::vector<std::string>& v, bool convert = true);
    void read(std::wstring&);
    void read(std::vector<std::wstring>&);
 
    void read(PatchFunc patchFunc, void* patchAddr);

    int readEnum(int maxValue);
 
    void throwException(std::function<void(const std::string&)> = nullptr);
 
    void readBlob(std::vector<Byte> v, int sz);
    void readBlob(const Byte*& v, size_type sz);

    template<typename T> inline void read(T& v);
 
    void read(std::pair<const Byte*, const Byte*>&);
    void read(std::vector<bool>&);
    void read(std::vector<Byte>&);
    void read(std::vector<short>&);
    void read(std::vector<int>&);
    void read(std::vector<long long int>&);
    void read(std::vector<float>&);
    void read(std::vector<double>&);
 
    // "Zero-copy" read
    void read(const char*& vdata, size_t& vsize, bool convert = true);
    void read(std::pair<const bool*, const bool*>&);
    void read(std::pair<const short*, const short*>&);
    void read(std::pair<const int*, const int*>&);
    void read(std::pair<const long long int*, const long long int*>&);
    void read(std::pair<const float*, const float*>&);
    void read(std::pair<const double*, const double*>&);
 
    // Extract a proxy of the base interface type
    std::shared_ptr<ObjectPrx> readProxy();

    // Extract a proxy of a user-defined interface type
    template</* T is-a ObjectPrx */> void read(std::shared_ptr<T>& v);

    // Extract an instance of a Slice class
    template</* T is-a Value */> void read(::std::shared_ptr<T>& v);
 
    void startValue();
    std::shared_ptr<SlicedData> endValue(bool preserve);
 
    void startException();
    std::shared_ptr<SlicedData> endException(bool preserve);
 
    // Read a list of "mandatory" parameters or data members in a single call
    template<typename T> void readAll(T& v);
    template<typename T, typename... Te> void readAll(T& v, Te&... ve);
 
    // Read a list of optional parameters or data members in a single call
    template<typename T> void readAll(std::initializer_list<int> tags, Optional<T>& v);
    template<typename T, typename... Te> void readAll(std::initializer_list<int> tags, Optional<T>& v, Optional<Te>&... ve);
 
    int readSize();
    int readAndCheckSeqSize(int minWireSize);
 
    std::string startSlice();
    void endSlice();
    void skipSlice();

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

    EncodingVersion getEncoding();

    void readPendingValues();

    size_type pos();
    void pos(size_type p);

    void skip(int sz);
    void skipSize();
 
    bool readOptional(int tag, OptionalFormat expectedFormat);
    void skipOptional(OptionalFormat format);
    void skipOptionals();

    template<typename T> void read(int tag, Ice::Optional<T>& v)
};
class InputStream
{
public:

    void read(bool&);
    void read(Byte&);
    void read(short&);
    void read(int&);
    void read(long long int&);
    void read(float&);
    void read(double&);

    void read(std::string& s, bool convert = true);
    void read(std::vector<std::string>& v, bool convert = true);
    void read(std::wstring&);
    void read(std::vector<std::wstring>&);
 
    void read(PatchFunc patchFunc, void* patchAddr);

    int readEnum(int maxValue);
 
    void throwException(const Ice::UserExceptionFactorPtr& = 0);
 
    void readBlob(std::vector<Byte> v, int sz);
    void readBlob(const Byte*& v, size_type sz);

    template<typename T> inline void read(T& v);
 
    void read(std::pair<const Byte*, const Byte*>&);
    void read(std::vector<bool>&);
    void read(std::vector<Byte>&);
    void read(std::vector<short>&);
    void read(std::vector<int>&);
    void read(std::vector<long long int>&);
    void read(std::vector<float>&);
    void read(std::vector<double>&);
 
    // "Zero-copy" read
    void read(const char*& vdata, size_t& vsize); // does not use string converter
    void read(const char*& vdata, size_t& vsize, std::string& holder); // uses string converter
    void read(std::pair<const bool*, const bool*>& v, IceUtil::ScopedArray<bool>& holder);
    void read(std::pair<const Short*, const Short*>& v, IceUtil::ScopedArray<Short>& holder);
    void read(std::pair<const Int*, const Int*>& v, IceUtil::ScopedArray<Int>& holder);
    void read(std::pair<const Long*, const Long*>& v, IceUtil::ScopedArray<Long>& holder);
    void read(std::pair<const Float*, const Float*>& v, IceUtil::ScopedArray<Float>& holder);
    void read(std::pair<const Double*, const Double*>& v, IceUtil::ScopedArray<Double>& holder);
    // Extract a proxy of the base interface type
    void read(ObjectPrx&);

    // Extract a proxy of a user-defined interface type
    template<typename T> void read(ProxyHandle<T>& v);

    // Extract an instance of a Slice class
    template<typename T> void read(Handle<T>& v);
 
    void startValue();
    SlicedDataPtr endValue(bool preserve);
 
    void startException();
    SlicedDataPtr endException(bool preserve);
 
    int readSize();
    int readAndCheckSeqSize(int minWireSize);
 
    std::string startSlice();
    void endSlice();
    void skipSlice();

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

    EncodingVersion getEncoding();

    void readPendingValues();

    size_type pos();
    void pos(size_type p);

    void skip(int sz);
    void skipSize();
 
    bool readOptional(int tag, OptionalFormat expectedFormat);
    void skipOptional(OptionalFormat format);
    void skipOptionals();

    template<typename T> void read(int tag, Ice::Optional<T>& v)
};

Some of these functions need more explanation:

  • void read(std::string& s, bool convert = true)
    The bool argument determines whether the strings unmarshaled by read are processed by the string converter, if one is installed. The default behavior is to convert the strings.
  • int readEnum(int maxValue)
    Unmarshals the integer value of an enumerator. The maxValue argument represents the highest enumerator value in the enumeration. Consider the following definitions:

    Slice
    enum Color { red, green, blue }
    enum Fruit { Apple, Pear=3, Orange }
    

    The maximum value for Color is 2, and the maximum value for Fruit is 4.
    In general, you should simply use read for your enum values. read with an enum parameter calls readEnum with the maxValue provided by the code generated by slice2cpp.

  • int readSize()
    The Ice encoding has a compact representation to indicate size. 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 indicates the smallest possible on-the-wire representation of a single sequence element. If the unmarshaling buffer contains insufficient data to unmarshal the sequence, the function throws UnmarshalOutOfBoundsException.
  • std::shared_ptr<ObjectPrx> readProxy() (C++11)
    void read(ObjectPrx&) (C++98)

    Returns an instance of the base proxy type, ObjectPrx. Calling read with a proxy parameter has the same effect.
  • void read(std::shared_ptr<T>& v) (C++11)
    void read(Handle<T>& v) (C++98)
    void read(PatchFunc patchFunc, void* patchAddr)

    These functions are used to extract instances of Slice classes. The Ice encoding for class instances requires extraction to occur in stages. The final overloading above accepts a function pointer, whose definition is shown below: 

    typedef void (*PatchFunc)(void*, const std::shared_ptr<Value>&);
    
    typedef void (*PatchFunc)(void*, const ValuePtr&);
    

    When the instance is available, the stream invokes the function and passes the value of patchAddr as well as a smart pointer for the new instance. The application must call readPendingValues to ensure that all instances are properly extracted. If you're not interested in receiving a callback when the instance is extracted, it is easier to call the read overload that accepts a smart pointer parameter. Note that calling endEncapsulation implicitly calls readPendingValues if necessary.

  • void throwException(std::function<void(const std::string&)> = nullptr) (C++11)
    void throwException(const UserExceptionFactoryPtr& factory = 0) (C++98)

    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 exception is thrown: for the 1.0 encoding, the exception is UnmarshalOutOfBoundsException, for the 1.1 encoding, the exception is UnknownUserException. You can optionally supply a factory function in C++11, or an object that implements the Ice::UserExceptionFactory bstract base class in C++98: 

    C++98
    class UserExceptionFactory : public IceUtil::Shared
    {   
    public:
        virtual void createAndThrow(const std::string& typeId) const = 0;
    };
    typedef ... UserExceptionFactoryPtr;

    As the stream iterates over the slices of an exception from most-derived to least-derived, it invokes createAndThrow passing the type ID of each slice, giving the application an opportunity to raise an instance of UserException. The stream expects the exception instance to call startException, unmarshal the remaining slices, and then call endException. If the factory does not throw an exception for a type ID, and no static information can be found for the type, the stream skips that slice.

  • void startValue()
    std::shared_ptr<SlicedData> endValue(bool preserve) (C++11)
    SlicedDataPtr endValue(bool preserve) (C++98)

    The startValue function must be called prior to reading the slices of a class instance. The endValue function must be called after all slices have been read. Pass true to endValue in order to preserve the slices of any unknown more-derived types, or false to discard the slices. If preserve is true and the stream actually preserved any slices, the return value of endValue is a non-nil SlicedData object that encapsulates the slice data. If the caller later wishes to forward the value with any preserved slices intact, it must supply this SlicedData object to the output stream.
  • void startException()
    std::shared_ptr<SlicedData> endException(bool preserve) (C++11)
    SlicedDataPtr endException(bool preserve) (C++98)

    The startException function must be called prior to reading the slices of an exception. The endException function must be called after all slices have been read. Pass true to endException in order to preserve the slices of any unknown more-derived types, or false to discard the slices. If preserve is true and the stream actually preserved any slices, the return value of endException is a non-nil SlicedData object that encapsulates the slice data. If the caller later wishes to forward the exception with any preserved slices intact, it must supply this SlicedData object to the output stream.
  • std::string 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 a class instance or user exception. The startSlice function returns the type ID of the next slice, which may be an empty string depending on the format used to encode the instance or exception.
  • EncodingVersion startEncapsulation()
    void endEncapsulation()
    EncodingVersion skipEncapsulation()
    Start, end, and skip an encapsulation, respectively. The startEncapsulation and skipEncapsulation functions return the encoding version used to encode the contents of the encapsulation.
  • EncodingVersion skipEmptyEncapsulation()
    Skips an encapsulation that is expected to be empty. The stream raises EncapsulationException if the encapsulation is not empty.
  • int getEncapsulationSize()
    Returns the size of the current encapsulation.
  • EncodingVersion getEncoding()
    Returns the encoding version currently in use by the stream. 
  • void readPendingValues()
    An application must call this function after all other data has been extracted. This function extracts the state of class instances and invokes their corresponding callback objects. For backward compatibility with encoding version 1.0, this function must only be called when non-optional data members or parameters use class types.
  • size_type pos()
    void pos(size_type p)
    Queries or modifies the current position of the stream.
  • void skip(int sz)
    Skips the given number of bytes.
  • void skipSize()
    Reads a size at the current position and skips that number of bytes.
  • bool readOptional(int tag, OptionalFormat fmt)
    Returns true if an optional value with the given tag and format is present, or false otherwise. If this function returns true, the data associated with that optional value must be read next. Optional values must be read in order by tag from least to greatest. The Ice::OptionalFormat enumeration is defined as follows: 

    enum class OptionalFormat : unsigned char
    {
        F1 = 0,             // Fixed 1-byte encoding
        F2 = 1,             // Fixed 2 bytes encoding
        F4 = 2,             // Fixed 4 bytes encoding
        F8 = 3,             // Fixed 8 bytes encoding
        Size = 4,           // "Size encoding" on 1 to 5 bytes, e.g. enum, class identifier
        VSize = 5,          // "Size encoding" on 1 to 5 bytes followed by data, e.g. string, fixed size
                            // struct, or containers whose size can be computed prior to marshaling
        FSize = 6,          // Fixed size on 4 bytes followed by data, e.g. variable-size struct, container.
        Class = 7
    };
    
    enum OptionalFormat
    {
        OptionalFormatF1 = 0, // See C++11
        OptionalFormatF2 = 1,
        OptionalFormatF4 = 2,
        OptionalFormatF8 = 3,
        OptionalFormatSize = 4,
        OptionalFormatVSize = 5,
        OptionalFormatFSize = 6,
        OptionalFormatClass = 7
    };
    

    Refer to the encoding discussion for more information on the meaning of these values.

  • void read(int tag, Ice::Optional<T>& v) (C++11)
    void read(int tag, IceUtil::Optional<T>& v) (C++98)

    If an optional value with the given tag is present, the Optional parameter is marked as set and its value is read.

  • void skipOptional(OptionalFormat format)
    void skipOptionals()
    Skips one optional value with the given format, or skips all remaining optional values, respectively.

  • void setClosure(void* p)
    void* getClosure() const
    Allows an arbitrary value to be associated with the stream.

Extracting Sequences of Built-In Types using Zero-Copy in C++

InputStream provides a number of overloads that accept a pair of pointers. For example, you can extract a sequence of bytes as follows:

C++
vector<Ice::Byte> data = ...;
Ice::InputStream in(communicator, data);
std::pair<const Ice::Byte*, const Ice::Byte*> p;
in.read(p);

The same extraction 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 representation of the type, otherwise the pointers refer to a temporary array allocated to hold the unmarshaled data. With the C++11 mapping, the InputStream itself allocates and holds these temporary arrays, whereas with the C++98, you need to supply this temporary array as a  IceUtil::ScopedArray parameter.

Here is an example to illustrate how to extract a sequence of integers, regardless of whether the machine's encoding of integers matches the on-the-wire representation or not:

...
Ice::InputStream in(communicator, data);
std::pair<const int*, const int*> p;
in.read(p);

for(const int* i = p.first; i != p.second; ++i)
{
    cout << *i << endl;
}
...
Ice::InputStream in(communicator, data);
std::pair<const int*, const int*> p;
IceUtil::ScopedArray<int> holder;
in.read(p, holder);

for(const 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 stream's internal marshaling buffer. Otherwise, the stream allocates an array, unmarshals the data into the array, and sets the pair of pointers to point into that array.

See Also