RTI Connext C API Version 7.3.0
|
A sample of any complex data type, which can be inspected and manipulated reflectively. More...
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:
Instead, you will do something like this:
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.
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.
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, you must use the DDS_DynamicData_get_member_info_by_index operation on the DynamicData object using an index value of 0. This operation fills in a DDS_DynamicDataMemberInfo, then you can access the populated DDS_DynamicDataMemberInfo::member_id field to get the current discriminator. 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:
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:
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:
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]".
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:
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.
Managing the lifecycle of DDS_DynamicData objects is simple. You have two choices:
You can also copy DDS_DynamicData objects:
You can test them for equality:
And you can print their contents:
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 | 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.
Get | Set |
---|---|
DDS_DynamicData_get_complex_member | DDS_DynamicData_set_complex_member |
In addition to getting or setting a field, you can "clear" its value; that is, set it to a default zero value.
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.
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.
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:
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.