RTI Connext Modern C++ API  Version 6.1.0
Working with IDL types

How IDL types map to C++ classes. More...


class  Foo
 An example topic-type. More...

Detailed Description

How IDL types map to C++ classes.

Example IDL types

The following IDL code defines the types we will use in these examples.

struct Foo {
long x; //@key
long y;
struct MyType {
long my_long;
string<512> my_string;
Foo my_foo;
sequence<long, 10> my_sequence;
Foo my_array[5];
Foo my_optional; //@Optional

To generate C++ code for these types see Create user data types using rtiddsgen

Constructors and operators

C++ types generated from IDL have value semantics and provide a default constructor, a copy constructor, a move constructor <<C++11>>, a constructor with parameters to set all the type's members, a destructor, a copy-assignment operator, and a move-assignment operator <<C++11>>. Types also include equality operators, the operator << to std::ostream <<extension>> and a namespace-level swap function.

// The default constructor recursively initializes all members of a sample
MyType default_sample;
// A copy constructor is also provided
MyType copied_sample = default_sample;
// For convenience, another constructor allows initializing all the members
MyType initialized_sample (
7, // my_long
std::string("Hello, World!"), // my_string
Foo(1, 2), // my_foo (initializing its members 'x' and 'y')
std::vector<int32_t>(3, 2), // my_sequence containing 3 times the value 2
dds::core::array<Foo, 5>(), // my_array
Foo(2, 3) // my_optional (initialized with a non-empty value)
// An assignment operator is also available
copied_sample = initialized_sample;
// Equals operator (!= also available)
if (copied_sample == initialized_sample) {
std::cout << "Samples are equal\n";
// You can move a value into a member:
std::vector<int32_t> tmp_vector {1, 2, 3, 4};
copied_sample.my_sequence() = std::move(tmp_vector);
std::cout << "You can print samples to an output stream: "
<< copied_sample
<< std::endl;

In addition to that, a number of traits provide additional information and utilities for IDL-generated types.

Accessing the type members

Setters and getters allow accessing the type members.

The following table summarizes how different IDL types map to C++.

IDL type C++ type
string std::string
bounded sequence (sequence<T, M>) rti::core::bounded_sequence<T, M>
unbounded sequence (sequence<T>) std::vector<T>
array (T member[N]) dds::core::array<T, N>
optional member (@optional T member; ) dds::core::optional<T>
external member (@external T member; ) dds::core::external<T>

The type rti::core::bounded_sequence offers similar functionality to that of a std::vector. But because it has an upper bound M, it provides two advantages:

1) A better memory management strategy for deserializing data samples in a DataReader, improving the overall performance of the middleware.

2) Its member functions check for out-of-bounds growth.

It is possible to use std::vector for bounded sequences by annotating them with @use_vector in IDL. For example:

struct SequenceExample {
@use_vector sequence<long, 10> bounded_sequence_as_vector;
sequence<long, 10> bounded_sequence;
sequence<long> unbounded_sequence;

The type dds::core::array is just an alias of std::array if available, or an alias of boost::array otherwise.

The following example shows how to use getters and setters and work with different IDL types.

MyType my_sample;
// Access a primitive type
if (my_sample.my_long() == 0) { // getter
my_sample.my_long(1); // setter
// Access a string
my_sample.my_string() = "Hello, World!"; // reference getter
std::cout << "my_string: " << my_sample.my_string() << std::endl;
// Access nested type
std::cout << "my_foo.x: " << my_sample.my_foo().x() << std::endl;
my_sample.my_foo().x(3); // set a member of the nested type
my_sample.my_foo() = Foo(1, 2); // or assign a full object
// Access a sequence
my_sample.my_sequence().resize(4); // by default length is zero
int my_ints[] = {3, 2, 4, 1};
std::copy(my_ints, my_ints + 4, my_sample.my_sequence().begin());
std::sort(my_sample.my_sequence().begin(), my_sample.my_sequence().end());
std::cout << "my_sequence[0]: " << my_sample.my_sequence()[0] << std::endl;
// Access an array
std::cout << "my_array[3]: " << my_sample.my_array()[3] << std::endl;
// Fill array with copies of Foo(4, 5)
std::fill(my_sample.my_array().begin(), my_sample.my_array().end(), Foo(4, 5));
// Access an optional member
if (!my_sample.my_optional().has_value()) { // by default, an optional member is unset
my_sample.my_optional(Foo(1, 2)); // assign a value
my_sample.my_optional() = Foo(1, 2); // this is equivalent
my_sample.my_optional().reset(); // make it empty
std::cout << "my_sample: " << my_sample << std::endl;


IDL enums map to an instantiation of the C++ dds::core::safe_enum class, which provides a scoped, type-safe enum.

For example, given the following IDL enum:

enum Color {

This is how to use it in C++:

Color color = Color::GREEN; // scoped constants
// The usual operators are available
if (color == Color::GREEN) {
std::cout << "It's green\n";
// if you need to access the integer value
switch (color.underlying()) {
case Color::RED: std::cout << "It's red\n"; break;
case Color::GREEN: std::cout << "It's green\n"; break;
case Color::BLUE: std::cout << "It's blue\n"; break;


IDL unions map to C++ classes with getters and setters for each member, plus a especial getter and setter for the discriminator, _d(). At any moment only one member, the one selected by the discriminator, is considered active. A member getter will throw dds::core::PreconditionNotMetError if the discriminator selects a different member.

Member setters set the correct discriminator value automatically.

The default constructor default-constructs all the members and selects the discriminator returned by the static member function default_discriminator().

The following example shows an IDL union and C++ code to manipulate it.