RTI Connext C API Version 7.4.0
DDS_DynamicData Struct Reference

A sample of any complex data type, which can be inspected and manipulated reflectively. More...

Detailed Description

A sample of any complex data type, which can be inspected and manipulated reflectively.

Objects of type DDS_DynamicData represent corresponding objects of the type identified by their DDS_TypeCode. Because the definition of these types may not have existed at compile time on the system on which the application is running, you will interact with the data using an API of reflective getters and setters.

For example, if you had access to your data types at compile time, you could do this:

theValue = theObject.theField;

Instead, you will do something like this:

theValue = get(theObject, "theField");

DDS_DynamicData objects can represent any complex data type, including those of type kinds DDS_TK_ARRAY, DDS_TK_SEQUENCE, DDS_TK_STRUCT, DDS_TK_UNION, and DDS_TK_VALUE. They cannot represent objects of basic types (e.g., integers and strings). Since those type definitions always exist on every system, you can examine their objects directly.

Member Names and IDs

The members of a data type can be identified in one of two ways: by their name or by their numeric ID. The former is often more transparent to human users; the latter is typically faster.

You define the name and ID of a type member when you add that member to that type. If you define your type in IDL or XML, the name will be the field name that appears in the type definition; the ID will be the one-based index of the field in declaration order. For example, in the following IDL structure, the ID of theLong is 2.

struct MyNestedType {
char theChar;
octet theOctetArray[10];
long long theMultidimensionalArray[4][6][12];
sequence<long> myArrayOfSeq[8];
};
struct MyType {
short theShort;
long theLong;
MyNestedType theNestedType;
};

Getting and Setting Union Discriminator Values

For unions (DDS_TK_UNION), the ID of a member is the discriminator value corresponding to that member. To access the current discriminator of a union, use the DDS_DynamicData_get_discriminator operation. Once you know the value of the discriminator, you can use it in the proper get/set_xxx() operations to access and set the member's value. Here is an example of accessing the discriminator and then using it to get a member value:

DynamicDataMemberId discriminatorValue = myDynamicData.get_discriminator();
DDS_Long myMemberValue = myDynamicData.get_long(NULL, discriminatorValue);
RTICdrLong DDS_Long
Defines a long integer data type, equivalent to IDL/CDR long.
Definition: common.ifc:244

To get information, like the DDS_TCKind, about the member that the discriminator value selects, use the DDS_DynamicData_get_member_info API, passing in NULL for the name and the discriminator for the member ID. If the discriminator value does not select any member in the union, DDS_DynamicData_get_member_info will return DDS_RETCODE_NO_DATA.

The discriminator value is automatically set as a side-effect of setting a member using one of the set_xxx() operations. For example, given the following IDL Union:

union Foo switch (short) {
case 1:
long x;
case 2:
case 3:
string y;
};
A representative user-defined data type.
Definition: data.ifc:112

Setting member "x" using the DDS_DynamicData_set_long API, passing either "x" as the name or 1 as the member ID, will set the discriminator value to 1. When members can be selected by multiple case values, like "y" in our example, if the memeber is set by name, the first case label is chosen as the discriminator. For example, if member "y" is set by name, the discriminator value will be set to 2. To set the discriminator to a different value you can either set the member by ID, passing in the desired discriminator value, or use the DDS_DynamicData_set_discriminator API.

Note: In past releases, accessing the discriminator value required passing in index 0 to the DDS_DynamicData_get_member_info_by_index operation and then looking at the populated info.member_id. That functionality has been deprecated and replaced with the DDS_DynamicData_get_discriminator operation.

Hierarchical Member Names

It is possible to refer to a nested member in a type without first having to use the DDS_DynamicData_bind_complex_member API. You can do this by using a hierarchical name. A hierarchical member name is a concatenation of member names separated by the '.' character. The hierarchical name describes the complete path from a top-level type to the nested member. For example, in the above type, any DynamicData API that receives a member name will accept "theNestedType.theChar" to refer to the char member in MyNestedType:

char myChar = myDynamicData.get_char("theNestedType.theChar", DDS_DYNAMIC_DATA_MEMBER_ID_UNSPECIFIED);
#define DDS_DYNAMIC_DATA_MEMBER_ID_UNSPECIFIED
A sentinel value that indicates that no member ID is needed in order to perform some operation.
Definition: dynamicdata.ifc:1456

In order to access the value of theChar without using a hierarchical name, you would have to first bind to theNestedType and then get the value:

myDynamicData.bind_complex_member(myBoundData, "theNestedType", DDS_DYNAMIC_DATA_MEMBER_ID_UNSPECIFIED);
DDS_Char myChar = myBoundData.get_char("theChar", DDS_DYNAMIC_DATA_MEMBER_ID_UNSPECIFIED);
RTICdrChar DDS_Char
Defines a character data type, equivalent to IDL/CDR char.
Definition: common.ifc:204

As you can see, using a hierarchical member name removes the need to call the DDS_DynamicData_bind_complex_member and DDS_DynamicData_unbind_complex_member APIs, and allows for access to nested members at any depth directly from the top-level type.

The member name can also contain indexes to address members in arrays and sequences. For example, to set the third member in the array theOctetArray, you can pass in "theNestedType.theOctetArray[2]" as the member name to the DDS_DynamicData_set_octet API. The index values when used as part of the member name are 0-based.

For multi-dimensional arrays, the indexes for each dimension should be listed comma-separated in between brackets. For example, to address a member of theMultidimensionalArray, the member name should be something like "theNestedType.theMultidimensionalArray[3,2,5]".

In complex types with arrays and sequences that contain other arrays and sequences, the hierarchical name may include multiple index values, one right after another. For example, in MyNestedType, myArrayOfSeq is an array of sequences. In order to set the third member of the sequence in the fourth member of the array, the member name would be "myNestedType.myArrayOfSeq[3][2]".

Arrays and Sequences

The "members" of array and sequence types, unlike those of structure and union types, don't have names or explicit member IDs. However, they may nevertheless be accessed by "ID": the ID is one more than the index. (The first element has ID 1, the second 2, etc.)

Multi-dimensional arrays are effectively flattened by this pattern. For example, for an array theArray[4][5], accessing ID 7 is equivalent to index 6, or the second element of the second group of 5.

To determine the length of a collection-typed member of a structure or union, you have two choices:

  1. Get the length along with the data: call the appropriate array accessor (see Getters and Setters) and check the resulting length.
  2. Get the length without getting the data itself: call DDS_DynamicData_get_member_info and check the resulting DDS_DynamicDataMemberInfo::element_count.

Available Functionality

The Dynamic Data API is large when measured by the number of methods it contains. But each method falls into one of a very small number of categories. You will find it easier to navigate this documentation if you understand these categories.

Lifecycle and Utility Methods

Managing the lifecycle of DDS_DynamicData objects is simple. You have two choices:

  1. Usually, you will go through a DDS_DynamicDataTypeSupport factory object, which will ensure that the type and property information for the new DDS_DynamicData object corresponds to a registered type in your system.
  2. In certain advanced cases, such as when you're navigating a nested structure, you will want to have a DDS_DynamicData object that is not bound up front to any particular type, or you will want to initialize the object in a custom way. In that case, you can call the constructor directly.
Lifecycle
DDS_DynamicDataTypeSupport DDS_DynamicData
DDS_DynamicDataTypeSupport_create_data DDS_DynamicData_new
DDS_DynamicDataTypeSupport_initialize_data DDS_DynamicData_initialize
DDS_DynamicDataTypeSupport_finalize_data DDS_DynamicData_finalize
DDS_DynamicDataTypeSupport_delete_data DDS_DynamicData_delete


You can also copy DDS_DynamicData objects:

You can test them for equality:

And you can print their contents:

Getters and Setters

Most methods get or set the value of some field. These methods are named according to the type of the field they access.

The names of types vary across languages. The programming API for each language reflects that programming language. However, if your chosen language does not use the same names as the language that you used to define your types (e.g., IDL), or if you need to interoperate among programming languages, you will need to understand these differences. They are explained the following table. (Note: for modern C++, see the RTI Connext Modern C++ API reference.)

Type Names Across Languages
Type IDL C, Traditional C++ Java Ada
16-bit integer short DDS_Short short Standard.DDS.Short
32-bit integer long DDS_Long int Standard.DDS.Long
64-bit integer long long DDS_LongLong long Standard.DDS.Long_Long
Unsigned 16-bit integer unsigned short DDS_UnsignedShort short Standard.DDS.Unsigned_Short
Unsigned 32-bit integer unsigned long DDS_UnsignedLong int Standard.DDS.Long
Unsigned 64-bit integer unsigned long long DDS_UnsignedLongLong long Standard.DDS.Unsigned_Long_Long
float float DDS_Float float Standard.DDS.Float
double double DDS_Double double Standard.DDS.Double
long double long double DDS_LongDouble N/A (see CORE-14091 known issue) Standard.DDS.Long_Double
character char DDS_Char char Standard.DDS.Char
wide character wchar DDS_Wchar char Standard.DDS.Wchar
octet octet DDS_Octet byte Standard.DDS.Octet
boolean boolean DDS_Boolean boolean Standard.DDS.Boolean
string string DDS_Char* String Standard.DDS.String
wstring wstring DDS_Wchar* String Standard.DDS.Wide_String

When working with a DDS_DynamicData object representing an array or sequence, calling one of the "get" methods below for an index that is out of bounds will result in DDS_RETCODE_NO_DATA. Calling "set" for an index that is past the end of a sequence will cause that sequence to automatically lengthen (filling with default contents).

When working with a DDS_DynamicData object whose type contains optional members, calling one of the "get" methods below on an unset optional member or any member that is part of an unset complex optional member will result in DDS_RETCODE_NO_DATA.

Basic Types
GetSet
DDS_DynamicData_get_long DDS_DynamicData_set_long
DDS_DynamicData_get_ulong DDS_DynamicData_set_ulong
DDS_DynamicData_get_short DDS_DynamicData_set_short
DDS_DynamicData_get_ushort DDS_DynamicData_set_ushort
DDS_DynamicData_get_longlong DDS_DynamicData_set_longlong
DDS_DynamicData_get_ulonglong DDS_DynamicData_set_ulonglong
DDS_DynamicData_get_float DDS_DynamicData_set_float
DDS_DynamicData_get_double DDS_DynamicData_set_double
DDS_DynamicData_get_longdouble DDS_DynamicData_set_longdouble
DDS_DynamicData_get_boolean DDS_DynamicData_set_boolean
DDS_DynamicData_get_octet DDS_DynamicData_set_octet
DDS_DynamicData_get_char DDS_DynamicData_set_char
DDS_DynamicData_get_wchar DDS_DynamicData_set_wchar
DDS_DynamicData_get_string DDS_DynamicData_set_string
DDS_DynamicData_get_wstring DDS_DynamicData_set_wstring


Structures, Arrays, and Other Complex Types
GetSet
DDS_DynamicData_get_complex_member DDS_DynamicData_set_complex_member


Arrays of Basic Types
GetSet
DDS_DynamicData_get_long_array DDS_DynamicData_set_long_array
DDS_DynamicData_get_ulong_array DDS_DynamicData_set_ulong_array
DDS_DynamicData_get_short_array DDS_DynamicData_set_short_array
DDS_DynamicData_get_ushort_array DDS_DynamicData_set_ushort_array
DDS_DynamicData_get_longlong_array DDS_DynamicData_set_longlong_array
DDS_DynamicData_get_ulonglong_array DDS_DynamicData_set_ulonglong_array
DDS_DynamicData_get_float_array DDS_DynamicData_set_float_array
DDS_DynamicData_get_double_array DDS_DynamicData_set_double_array
DDS_DynamicData_get_longdouble_array DDS_DynamicData_set_longdouble_array
DDS_DynamicData_get_boolean_array DDS_DynamicData_set_boolean
DDS_DynamicData_get_octet_array DDS_DynamicData_set_octet_array
DDS_DynamicData_get_char_array DDS_DynamicData_set_char_array
DDS_DynamicData_get_wchar_array DDS_DynamicData_set_wchar_array


Sequences of Basic Types
GetSet
DDS_DynamicData_get_long_seq DDS_DynamicData_set_long_seq
DDS_DynamicData_get_ulong_seq DDS_DynamicData_set_ulong_seq
DDS_DynamicData_get_short_seq DDS_DynamicData_set_short_seq
DDS_DynamicData_get_ushort_seq DDS_DynamicData_set_ushort_seq
DDS_DynamicData_get_longlong_seq DDS_DynamicData_set_longlong_seq
DDS_DynamicData_get_ulonglong_seq DDS_DynamicData_set_ulonglong_seq
DDS_DynamicData_get_float_seq DDS_DynamicData_set_float_seq
DDS_DynamicData_get_double_seq DDS_DynamicData_set_double_seq
DDS_DynamicData_get_longdouble_seq DDS_DynamicData_set_longdouble_seq
DDS_DynamicData_get_boolean_seq DDS_DynamicData_set_boolean_seq
DDS_DynamicData_get_octet_seq DDS_DynamicData_set_octet_seq
DDS_DynamicData_get_char_seq DDS_DynamicData_set_char_seq
DDS_DynamicData_get_wchar_seq DDS_DynamicData_set_wchar_seq

In addition to getting or setting a field, you can "clear" its value; that is, set it to a default zero value.

Query and Iteration

Not all components of your application will have static knowledge of all of the fields of your type. Sometimes, you will want to query meta-data about the fields that appear in a given data sample.

Type/Object Association

Sometimes, you may want to change the association between a data object and its type. This is not something you can do with a typical object, but with DDS_DynamicData objects, it is a powerful capability. It allows you to, for example, examine nested structures without copying them by using a "bound" DDS_DynamicData object as a view into an enclosing DDS_DynamicData object.

Performance Considerations

By default, a DynamicData object stores its content in an implementation-specific representation that allows getting and setting its fields by name or ID.

When a sample is received, the CDR-formatted data is deserialized into this buffer.

When a sample is published, the data is serialized into CDR from the buffer and sent to the network.

However, there are scenarios where DynamicData is used for purposes that don't involve manipulating the object's content directly. Two common examples are:

  • Recording/Replaying: Storing samples in persistent storage for later analysis or replay.
  • Bridging: Transferring samples from one domain to another by transmitting samples received with a DynamicDataReader to another domain using a DynamicDataWriter.

RTI provides dedicated services, namely RTI Recording Service and RTI Routing Service, to address these use cases. However, in certain situations, you might be unable to utilize these services.

For scenarios where inspecting or modifying the content of DynamicData fields is unnecessary, DynamicData offers a high-performance method to configure a DataReader to avoid deserializing incoming samples into the internal representation. Additionally, the DynamicData API provides a method to link a CDR-formatted serialization buffer directly with a DynamicData object, facilitating direct transmission without converting the data from the internal representation to CDR.

To skip the deserialization of a DynamicData object on the DataReader side, you can set the field DDS_DynamicDataTypeSerializationProperty_t::skip_deserialization to DDS_BOOLEAN_TRUE.

Once a sample is received, you can access the serialized buffer directly by using the DDS_DynamicData_get_cdr_buffer. This buffer can be stored in persistent storage in a Recording/Replaying use case.

To link a serialization buffer directly with a DynamicData object, you can use the DDS_DynamicData_set_cdr_buffer method. This method allows you to set the buffer that will be used to serialize the DynamicData object when it is published.

MT Safety:
UNSAFE. In general, using a single DDS_DynamicData object concurrently from multiple threads is unsafe.
Examples
HelloWorldPlugin.c.