C++98 Mapping for Sequences

On this page:

Default Sequence Mapping in C++

Here is the definition of our FruitPlatter sequence once more:

Slice
sequence<Fruit> FruitPlatter;

The Slice compiler generates the following definition for the FruitPlatter sequence:

C++
typedef std::vector<Fruit> FruitPlatter;

As you can see, the sequence simply maps to a standard std::vector, so you can use the sequence like any other vector. For example:

C++
// Make a small platter with one Apple and one Orange
//
FruitPlatter p;
p.push_back(Apple);
p.push_back(Orange);

Custom Sequence Mapping in C++

You can override the default mapping of Slice sequences to C++ vectors with the cpp:type and cpp:view-type metadata directives

For example:

Slice
[["cpp:include:list"]]

module Food
{
    enum Fruit { Apple, Pear, Orange };

    ["cpp:type:std::list< ::Food::Fruit>"]
    sequence<Fruit> FruitPlatter;
}

With this metadata directive, the sequence now maps to a C++ std::list:

C++
#include <list>

namespace Food 
{
    typedef std::list<Food::Fruit> FruitPlatter;

    // ...
}

Custom Mapping for Sequence Parameters in C++

In addition to the default and custom mappings of sequence types as a whole, you can use metadata to customize the mapping of a single operation parameter of type sequence.

Ice provides two metadata directives for this purpose, ["cpp:array"] and ["cpp:range"].

Array Mapping for Sequence Parameters in C++

The array mapping for sequence parameters applies only to: 

 For example:

Slice
interface File
{
    void write(["cpp:array"] Ice::ByteSeq contents);
}

The cpp:array metadata directive instructs the compiler to map the contents parameter to a pair of pointers. With this directive, the write method on the proxy has the following signature:

C++
void write(const std::pair<const Ice::Byte*, const Ice::Byte*>& contents);

To pass a byte sequence to the server, you pass a pair of pointers; the first pointer points at the beginning of the sequence, and the second pointer points one element past the end of the sequence.

Similarly, for the server side, the write method on the skeleton has the following signature:

C++
virtual void write(const ::std::pair<const ::Ice::Byte*, const ::Ice::Byte*>&,
                   const ::Ice::Current& = ::Ice::Current()) = 0;

The passed pointers denote the beginning and end of the sequence as a range [first, last) (that is, they use the usual semantics for iterators).

The array mapping is useful to achieve zero-copy passing of sequences. The pointers point directly into the server-side transport buffer; this allows the server-side run time to avoid creating a vector to pass to the operation implementation, thereby avoiding both allocating memory for the sequence and copying its contents into that memory.

You can use the array mapping for any sequence type. However, it provides a performance advantage only for byte sequences (on all platforms) and for sequences of integral or floating point types (on some platforms).

Range Mapping for Sequence Parameters in C++

The range mapping for sequences is similar to the array mapping and exists for the same purpose, namely, to enable zero-copy of sequence parameters:

Slice
interface File
{
    void write(["cpp:range"] Ice::ByteSeq contents);
}

The cpp:range metadata directive instructs the compiler to map the contents parameter to a pair of const_iterator. With this directive, the write method on the proxy has the following signature:

C++
void write(const std::pair<Ice::ByteSeq::const_iterator, Ice::ByteSeq::const_iterator>& contents);

Similarly, for the server side, the write method on the skeleton has the following signature:

C++
virtual void write(
    const ::std::pair<::Ice::ByteSeq::const_iterator, ::Ice::ByteSeq::const_iterator>&,
    const ::Ice::Current& = ::Ice::Current()) = 0;

The passed iterators denote the beginning and end of the sequence as a range [first, last) (that is, they use the usual semantics for iterators).

The motivation for the range mapping is the same as for the array mapping: the passed iterators point directly into the server-side transport buffer and so avoid the need to create a temporary vector to pass to the operation.

As for the array mapping, the range mapping can be used with any sequence type, but offers a performance advantage only for byte sequences (on all platforms) and for sequences of integral type (x86 platforms only).

You can optionally add a type name to the cpp:range metadata directive, for example:

Slice
interface File
{
    void write(["cpp:range:std::deque<Ice::Byte>"] Ice::ByteSeq contents);
}

This instructs the compiler to generate a pair of const_iterator for the specified type:

C++
virtual void write(
    const ::std::pair<std::deque<Ice::Byte>::const_iterator,
                      std::deque<Ice::Byte>::const_iterator>&,
    const ::Ice::Current& = ::Ice::Current()) = 0;

This is useful if you want to combine the range mapping with a custom sequence type that behaves like an standard container.

See Also