The OutputStream Interface in C++

On this page:

Initializing an OutputStream in C++

The OutputStream class provides a number of overloaded constructors:

namespace Ice
{
    class OutputStream
    {
    public:
        OutputStream();

        OutputStream(const std::shared_ptr<Communicator>& communicator);
 
        OutputStream(const std::shared_ptr<Communicator>& communicator, const EncodingVersion& version);

        OutputStream(const std::shared_ptr<Communicator>& communicator, const EncodingVersion& version,
                 const std::pair<const Byte*, const Byte*>& buf);
    };
} 
namespace Ice
{
    class OutputStream
    {
    public:
        OutputStream();

        OutputStream(const CommunicatorPtr& communicator);
 
        OutputStream(const CommunicatorPtr& communicator, const EncodingVersion& version);

        OutputStream(const CommunicatorPtr& communicator, const EncodingVersion& version,
                     const std::pair<const Byte*, const Byte*>& buf);
    };
} 

The constructors optionally accept the following arguments:

  • A communicator instance
  • An encoding version
  • A pair of bytes denoting the beginning and end of a memory block to be used as the stream's initial marshaling buffer. This is useful to avoid memory allocations when the size of the encoded data is predictable. The stream will automatically allocate a larger buffer if the encoded data exceeds the size of the given memory block.

We recommend supplying a communicator instance. The stream 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.

Instances of OutputStream can be allocated statically or dynamically.

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 OutputStream
{
public:
    void initialize(const std::shared_ptr<Communicator>& communicator);
    void initialize(const std::shared_ptr<Communicator>& communicator, const EncodingVersion& version);
    ...
};
class OutputStream
{
public:
    void initialize(const CommunicatorPtr& communicator);
    void initialize(const CommunicatorPtr& communicator, const EncodingVersion& version);
    ...
};

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

Use the following function to manually configure the stream:

C++
class OutputStream
{
public:
    void setFormat(FormatType);
    ...
};

For instances of Slice classes, the format determines how the slices of an instance are encoded. If the stream is initialized with a communicator, this setting defaults to the value of Ice.Default.SlicedFormat, otherwise the setting defaults to the compact format.

Inserting into an OutputStream in C++

OutputStream provides a number of overloaded write member functions that allow you to insert any parameter into the stream simply by calling write.

For example, you can insert a double value followed by a string into a stream as follows:

C++
Ice::OutputStream out(communicator);
Ice::Double d = 3.14;
out.write(d);
string s = "Hello";
out.write(s);

Likewise, you can insert a sequence of built-in type, or a complex, with the same syntax:

C++
Ice::OutputStream out(communicator);
IntSeq s = ...;
out.write(s);
 
ComplexType c = ...;
out.write(c);

With the C++11 mapping, you can also write a list of parameters in one call with writeAll. For example, the previous sample can be rewritten as:

C++11
Ice::OutputStream out(communicator);
IntSeq s = ...;
ComplexType c = ...;
out.writeAll(s, c);

Here are the functions for inserting data into an stream:

class OutputStream : ... 
{
public:

    void write(bool);
    void write(Byte);
    void write(short);
    void write(int);
    void write(long long int);
    void write(float);
    void write(double);
    void write(const std::string&, bool = true);
    void write(const char*, size_t, bool = true);
    void write(const char*, bool = true);
    void write(const std::string*, const std::string*, bool = true);
 
    void write(const std::wstring&);
    void write(const std::wstring*, const std::wstring*);
 
    template<typename T> void write(const std::vector<T>& v);

    template<typename T> void write(const T* begin, const T* end);

    void writeBlob(const std::vector<Byte>&);
    void writeBlob(const Byte* v, size_type sz);

    void writeEnum(int v, int maxValue);
 
    bool writeOptional(int tag, OptionalFormat fmt);

    template<typename T> void write(int tag, const Optional<T>& v);

    template<typename T> void write(const T& v);
 
    // Insert a proxy of the base type
    void writeProxy(const std::shared_ptr<ObjectPrx>&);

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

    // Insert an instance of a Slice class
    template</* T is-a Value */>
    void write(const std::shared_ptr<T>& v);

    void startValue(const std::shared_ptr<SlicedData>& sd);
    void endValue();

    void startException(const std::shared_ptr<SlicedData>& sd);
    void endException();
 
    // Write all parameters in one call
    template<typename T> void writeAll(const T& v);
    template<typename T, typename... Te> void writeAll(const T& v, const Te&... ve);
 
    // Write all optional parameters in one call
    template<typename T> void writeAll(std::initializer_list<int> tags, const Optional<T>& v);
    template<typename T, typename... Te> void writeAll(std::initializer_list<int> tags, const Optional<T>& v, const Optional<Te>&... ve);

    void writeSize(int sz);
    size_type startSize();
    void endSize(size_type pos);
    void rewriteSize(int v, iterator dest);
    void write(int v, iterator dest);
    void rewrite(int v, size_type pos);

    void writeException(const UserException& e);

    void startSlice(const std::string& typeId, int compactId, bool last);
    void endSlice();

    void startEncapsulation(const EncodingVersion& v, FormatType fmt);
    void startEncapsulation();
    void endEncapsulation();
    void writeEmptyEncapsulation(const EncodingVersion& v);
    void writeEncapsulation(const Byte* v, int sz);

    EncodingVersion getEncoding() const;

    void writePendingValues();

    void finished(std::vector<Byte>& v);
    std::pair<const Byte*, const Byte*> finished();

    void resize(size_type sz);

    size_type pos();
    void pos(size_type n);
};
class OutputStream : ... 
{
public:

    void write(bool);
    void write(Byte);
    void write(short);
    void write(int);
    void write(long long int);
    void write(float);
    void write(double);
    void write(const std::string&, bool = true);
    void write(const char*, size_t, bool = true);
    void write(const char*, bool = true);
    void write(const std::string*, const std::string*, bool = true);
 
    void write(const std::wstring&);
    void write(const std::wstring*, const std::wstring*);
 
    template<typename T> void write(const std::vector<T>& v);

    template<typename T> void write(const T* begin, const T* end);

    void writeBlob(const std::vector<Byte>&);
    void writeBlob(const Byte* v, size_type sz);

    void writeEnum(int v, int maxValue);
 
    bool writeOptional(int tag, OptionalFormat fmt);

    template<typename T> void write(int tag, const IceUtil::Optional<T>& v);

    template<typename T> void write(const T& v);
 
    // Insert a proxy of the base interface type
    void write(const ObjectPrx&);

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

    // Insert an instance of the base class type
    void write(const ObjectPtr& v);

    // Insert an instance of a user-defined class type
    template<typename T> void
    write(const Handle<T>& v);

    void startValue(const SlicedDataPtr& sd);
    void endValue();

    void startException(const SlicedDataPtr& sd);
    void endException();

    void writeSize(int sz);
    size_type startSize();
    void endSize(size_type pos);
    void rewriteSize(int v, iterator dest);
    void write(int v, iterator dest);
    void rewrite(int v, size_type pos);

    void writeException(const UserException& e);

    void startSlice(const std::string& typeId, int compactId, bool last);
    void endSlice();

    void startEncapsulation(const EncodingVersion& v, FormatType fmt);
    void startEncapsulation();
    void endEncapsulation();
    void writeEmptyEncapsulation(const EncodingVersion& v);
    void writeEncapsulation(const Byte* v, int sz);

    EncodingVersion getEncoding() const;

    void writePendingValues();

    void finished(std::vector<Byte>& v);
    std::pair<const Byte*, const Byte*> finished();

    void resize(size_type sz);

    size_type pos();
    void pos(size_type n);
};

Some of the OutputStream functions need more explanation:

  • void write(const std::string& v, bool convert = true)
    void write(const char* v, size_t vlen, bool convert = true)
    void write(const char* v, bool convert = true)
    void write(const std::string* beg, const std::string* end, bool convert = true)
    The boolean argument determines whether the strings marshaled by these functions are processed by the narrow string converter, if one has been provided. The default behavior is to convert the strings.
  • template<typename T> void write(const T* begin, const T* end)
    Marshals the given range as a sequence of type T.

  • void writeBlob(const std::vector<Byte>&)
    void writeBlob(const Byte* v, size_type sz)
    Copies the specified blob of bytes to the stream without modification.

  • void writeEnum(int val, int maxValue)
    Writes 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 write for your enum values. write with an enum parameter calls writeEnum with the maxValue provided by the code generated by slice2cpp.

  • bool writeOptional(int tag, OptionalFormat fmt)
    Prepares the stream to write an optional value with the given tag and format. Returns true if the value should be written, or false otherwise. A return value of false indicates that the encoding version in use by the stream does not support optional values. If this function returns true, the data associated with that optional value must be written next. Optional values must be written in order by tag from least to greatest. The 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.

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

    If the given optional value is set, this function writes the optional with its value.

  • void writeSize(int sz)
    The Ice encoding has a compact representation to indicate size. This function converts the given non-negative integer into the proper encoded representation.
  • size_type startSize()
    void endSize(size_type pos)
    The encoding for optional values uses a 32-bit integer to hold the size of variable-length types. Calling startSize writes a placeholder value for the size and returns the starting position of the size value; after writing the data, call endSize to patch the placeholder with the actual size at the given position.
  • void rewriteSize(int v, iterator dest)
    Replaces a size value at the given destination in the stream. This function does not change the stream's current position.
  • void write(int v, iterator dest)
    void rewrite(int v, size_type pos)

    Overwrite a 32-bit integer value at the given destination or position in the stream. These functions do not change the stream's current position.
  • void writeProxy(const std::shared_ptr<ObjectPrx>&) (C++11)
    template</* T is-a ObjectPrx */> void write(const std::shared_ptr<T>& v) (C++11)
    void write(const ObjectPrx&) (C++98)
    template<typename T> void write(const ProxyHandle<T>& v) (C++98)

    Inserts a proxy into the stream.
  • template</* T is-a Value */> void write(const std::shared_ptr<T>& v) (C++11)
    void write(const ObjectPtr& v) (C++98)
    template<typename T> void write(const Handle<T>& v) (C++98)

    Inserts an instance of a Slice class. The Ice encoding for class instances may cause the insertion of this instance to be delayed, in which case the stream retains a reference to the given instance and the stream does not insert its state it until writePendingValues is invoked on the stream.

  • void writeException(const Ice::UserException& ex)
    Inserts a user exception. It is equivalent to calling write with a user exception.
  • void startValue(const std::shared_ptr<SlicedData>& sd) (C++11)
    void startValue(const SlicedDataPtr& sd) (C++98)

    void endValue()
    When marshaling the slices of a class instance, the application must first call startValue, then marshal the slices, and finally call endValue. The caller can pass a SlicedData object containing the preserved slices of unknown more-derived types, or 0 if there are no preserved slices.

  • void startException(const std::shared_ptr<SlicedData>& sd) (C++11)
    void startException(const SlicedDataPtr& sd) (C++98)

    void endException()
    When marshaling the slices of an exception, the application must first call startException, then marshal the slices, and finally call endException. The caller can pass a SlicedData object containing the preserved slices of unknown more-derived types, or 0 if there are no preserved slices.
  • void startSlice(const std::string& typeId, int compactId, bool last)
    void endSlice()
    Starts and ends a slice of class or exception member data. The call to startSlice must include the type ID for the current slice, the corresponding compact ID for the type (if any), and a boolean indicating whether this is the last slice of the class instance or exception. The compact ID is only relevant for class instances; pass a negative value to indicate the encoding should use the string type ID.
  • void startEncapsulation(const EncodingVersion& v, FormatType fmt)
    void startEncapsulation()

    void endEncapsulation()
    Starts and ends an encapsulation, respectively. The first overloading of startEncapsulation allows you to specify the encoding version as well as the format to use for any class instances and exceptions marshaled within this encapsulation.
  • void writeEmptyEncapsulation(const EncodingVersion& v)
    Writes an encapsulation having the given encoding version with no encoded data.

  • void writeEncapsulation(const Byte* v, int sz)
    Copies the bytes representing an encapsulation from the given address into the stream.
  • EncodingVersion getEncoding()
    Returns the encoding version currently being used by the stream.
  • void writePendingValues()
    Encodes the state of class instances whose insertion was delayed during a previous call to write. With encoding version 1.0, this function must only be called exactly once when non-optional data members or parameters use class types. This function is no-op with encoding version 1.1.
  • void finished(std::vector<Byte>& data)
    std::pair<const Byte*, const Byte*> finished()

    Indicates that marshaling is complete. This member function must only be called once. In the first overloading, the given byte sequence is filled with a copy of the encoded data. The second overloading avoids a copy by returning a pair of pointers to the stream's internal memory; these pointers are valid for the lifetime of the OutputStream object.
  • void resize(size_type sz)
    Allocates space for sz more bytes in the buffer. The stream implementation internally uses this function prior to copying more data into the buffer.
  • size_type pos()
    void pos(size_type n)

    Returns or changes the stream's current position, respectively.

See Also