RTI Connext Modern C++ API  Version 6.0.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
DynamicType and DynamicData Use Cases

Using DynamicType and DynamicData. More...

Using DynamicType and DynamicData.

The following #includes are needed for the examples on this page

#include <dds/core/ddscore.hpp>
#include <dds/pub/ddspub.hpp>
#include <dds/sub/ddssub.hpp>
#include "Foo.hpp"

Creating a DynamicType

In this section we'll see the different ways to create a dds::core::xtypes::DynamicType that represents the following IDL type MyType:

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
};

Create a DynamicType by instantiating it and adding members

(Note this example uses C++11 code that can easily be converted to similar C++03 code)

{
using namespace dds::core::xtypes;
// First, let's create Foo. We're using an initializer_list of Members
"Foo", {
Member("x", primitive_type<int32_t>()).key(true),
Member("y", primitive_type<int32_t>())
}
);
// Create MyType and add members
StructType mytype("MyType");
mytype.add_member(Member("my_long", primitive_type<int32_t>()));
mytype.add_member(Member("my_string", StringType(512)));
mytype.add_member(Member("my_foo", foo));
mytype.add_member(Member("my_sequence", SequenceType(primitive_type<int32_t>(), 10)));
mytype.add_member(Member("my_array", ArrayType(foo, 5)));
mytype.add_member(Member("my_optional", foo).optional(true));
return mytype;
}

Create a DynamicType from an XML description

We can define the type in this XML file, MyType.xml:

<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<struct name= "Foo">
<member name="x" type="int32" key="true"/>
<member name="y" type="int32"/>
</struct>
<struct name= "MyType">
<member name="my_long" type="int32"/>
<member name="my_string" stringMaxLength="512" type="string"/>
<member name="my_foo" type="nonBasic" nonBasicTypeName= "Foo"/>
<member name="my_sequence" sequenceMaxLength="10" type="int32"/>
<member name="my_array" type="nonBasic" nonBasicTypeName= "Foo" arrayDimensions="5"/>
<member name="my_optional" type="nonBasic" nonBasicTypeName= "Foo" optional="true"/>
</struct>
</types>

And then load it as follows:

using namespace dds::core::xtypes;
// Create a QosProvider (or use the default one)
dds::core::QosProvider qos_provider("path/to/MyType.xml");
// Get the type called MyType
const DynamicType& mytype = qos_provider->type("MyType");
// Downcast to StructType if needed
const StructType& mytype_struct = static_cast<const StructType&>(mytype);
See Also
Qos Provider Use Cases

Get the DynamicType from its equivalent IDL-generated type

<<extension>> When we have the IDL type definition at hand, obtaining its equivalent DynamicType is as simple as:

Create a DynamicType using tuples

<<C++11>> <<experimental>> <<extension>> Note this code requires the additional header <rti/core/xtypes/DynamicDataTuples.hpp>.

Since this feature only supports structures containing primitive types or strings, we'll create the following simpler IDL type:

struct MyOtherType {
long m1;
double m2;
string m3;
};

We can create the type in a single line:

For convenience, we can also use a std::tuple, which allows us to typedef it:

typedef std::tuple<int32_t, double, std::string> MyOtherTypeTuple;
mytype = rti::core::xtypes::create_type_from_tuple<MyOtherTypeTuple>("MyOtherType");

Using DynamicData

Now that we know how to create a DynamicType, we will see how to use DynamicData to publish and subscribe to data that we can manipulate reflectively.

We will use the DynamicType that we created before.

Publishing data using DynamicData

Make sure you are already familiar with the regular publication example.

Unlike the regular publication example, using dynamic data we can write code where the type is unknown at compilation type.

using namespace dds::core::xtypes;
StructType mytype = create_mytype(); // create the type as we saw before
// The template parameter is DynamicData. The third argument is the
// DynamicType, instead of the type name.
dds::topic::Topic<DynamicData> topic(participant, "MyTopic", mytype);
// Create a data sample and assign its values
DynamicData sample(mytype);
sample.value("my_long", 23); // my_long = 23
sample.value<std::string>("my_string", "hello"); // my_string = hello
// To set my_foo we have two options:
// 1) Create a new DynamicData and assigning it:
// We can get the DynamicType of Foo from MyType::my_foo
DynamicData foo_data(mytype.member("my_foo").type());
foo_data.value("x", 1);
foo_data.value("y", 2);
sample.value("my_foo", foo_data);
// 2) Obtain the loaned member and assign its values
rti::core::xtypes::LoanedDynamicData loaned_member = sample.loan_value("my_foo");
loaned_member.get().value("x", 2);
loaned_member.get().value("y", 3);
loaned_member.return_loan(); // The destructor would do this as well.
// To set the values of my_sequence we can use a vector
std::vector<int32_t> sequence_values;
sequence_values.push_back(10);
sequence_values.push_back(20);
sequence_values.push_back(30);
sample.set_values("my_sequence", sequence_values);
// To set the values of my_array, since the element type is another struct
// we will use a LoanedDynamicData to access the array elements. To modify
// each element we will use another LoanedDynamicData
loaned_member = sample.loan_value("my_array");
rti::core::xtypes::LoanedDynamicData array_element = loaned_member.get().loan_value(1);
array_element.get().value("x", 10);
array_element.get().value("y", 11);
loaned_member.get().loan_value(array_element, 2); // reuse array_element
array_element.get().value("x", 20);
array_element.get().value("y", 21);
array_element.return_loan();
loaned_member.return_loan();
// We're not setting the remaining 3 elements, so they will have default values
// We manipulate optional members as regular members. By setting a value
// we are implicitly asserting its presence in the sample.
sample.value("my_optional", foo_data);
// This is how we can unset it:
sample.clear_optional_member("my_optional");
// Finally we just write the sample
writer.write(sample);
std::cout << sample << std::endl;

Subscribing to data using DynamicData

using namespace dds::core::xtypes;
using namespace rti::core::xtypes;
using namespace std; // for cout and endl
StructType mytype = create_mytype(); // create the type as we saw before
// The template parameter is DynamicData. The third argument is the
// DynamicType, instead of the type name.
dds::topic::Topic<DynamicData> topic(participant, "MyTopic", mytype);
// ...
// Read/take samples normally
dds::sub::LoanedSamples<DynamicData> samples = reader.take();
for (auto sample : samples) {
if (sample.info().valid()) {
DynamicData& data = const_cast<DynamicData&>(sample.data());
cout << data.value<int32_t>("my_long") << endl;
// To inspect a complex member we can get a copy of it:
DynamicData foo_copy = data.value<DynamicData>("my_foo");
cout << foo_copy.value<int32_t>("x") << endl;
// Or a loan:
LoanedDynamicData loaned_member = data.loan_value("my_foo");
cout << loaned_member.get().value<int32_t>("y") << endl;
loaned_member.return_loan(); // Note: the destructor also returns the loan if needed
// Note that we could have taken advantage of the destructor to save
// the call to return_loan:
cout << data.loan_value("my_foo").get().value<int32_t>("y") << endl;
// To get the values of my_sequence we can use a vector:
std::vector<int32_t> sequence_values = data.get_values<int32_t>("my_sequence");
// We could also have reused a vector:
data.get_values("my_sequence", sequence_values);
// To get the values of my_array, since the element type is another
// struct we will use a LoanedDynamicData to access the array
// elements.
loaned_member = data.loan_value("my_array"); // Note: this move-assignment only works in C++11
for (size_t i = 1; i <= loaned_member.get().member_count(); i++) {
LoanedDynamicData array_element = loaned_member.get().loan_value(i);
cout << array_element.get().value<int32_t>("x") << endl;
cout << array_element.get().value<int32_t>("y") << endl;
}
// When getting an unset optional member, value() will throw
// dds::core::InvalidArgumentError, but we can check if it's set:
if (data.member_exists("my_optional")) {
foo_copy = data.value<DynamicData>("my_optional");
cout << foo_copy.value<int32_t>("x") << endl;
cout << foo_copy.value<int32_t>("y") << endl;
} else {
cout << "my_optional is not set" << endl;
}
}
}

Manipulating data reflectively using C++ tuples

<<C++11>> <<experimental>> <<extension>>

This feature allows publishing and subscribing to simple data types without code generation and directly using C++ types instead of the DynamicData getters and setters.

This is an example publisher:

using namespace dds::core::xtypes;
// Create the type
int32_t, double, std::string>("MyType");
// Create participant, topic and writer
dds::topic::Topic<DynamicData> topic(participant, "MyTopic", mytype);
// Create a data sample and assign its values
DynamicData sample(mytype);
sample, std::make_tuple(10, 50.5, std::string("Hello")));
// Write the sample
writer.write(sample);
std::cout << sample << std::endl;

And this is a subscriber:

using namespace dds::core::xtypes;
using namespace rti::core::xtypes;
// Create the type
int32_t, double, std::string>("MyType");
dds::topic::Topic<DynamicData> topic(participant, "MyTopic", mytype);
// Read/take samples normally
dds::sub::LoanedSamples<DynamicData> samples = reader.take();
for (auto sample : samples) {
if (sample.info().valid()) {
// We obtain a std::tuple from the DynamicData sample
int32_t, double, std::string>(sample.data());
std::cout << std::get<0>(my_tuple) << std::endl;
std::cout << std::get<1>(my_tuple) << std::endl;
std::cout << std::get<2>(my_tuple) << std::endl;
}
}

RTI Connext Modern C++ API Version 6.0.0 Copyright © Sun Mar 3 2019 Real-Time Innovations, Inc