C++ Mapping for Sequences
On this page:
Default Sequence Mapping in C++
Here is the definition of our FruitPlatter
sequence once more:
sequence<Fruit> FruitPlatter;
The Slice compiler generates the following definition for the FruitPlatter
sequence:
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:
// 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:
[["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
:
#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:
- In parameters, on the client-side and on the server-side
- Out and return parameters provided by the Ice run-time to AMI type-safe callbacks and AMI lambdas
- Out and return parameters provided to AMD callbacks
For example:
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:
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:
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:
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:
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:
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:
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:
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.