4.1. Data Types¶
How data is stored or laid out in memory can vary from language to language, compiler to compiler, operating system to operating system, and processor to processor. This combination of language/compiler/operating system/processor is called a platform. Any modern middleware must be able to take data from one specific platform (for example, C/gcc.7.3.0/Linux®/PPC) and transparently deliver it to another (for example, C/gcc.7.3.0/Linux/Arm® v8). This process is commonly called serialization/deserialization, or marshalling/demarshalling.
Connext DDS Micro data samples sent on the same Connext DDS Micro topic share a data type. This type defines the fields that exist in the DDS data samples and what their constituent types are. The middleware stores and propagates this meta-information separately from the individual DDS data samples, allowing it to propagate DDS samples efficiently while handling byte ordering and alignment issues for you.
To publish and/or subscribe to data with Connext DDS Micro, you will carry out the following steps:
Select a type to describe your data and use the RTI Code Generator to define a type at compile-time using a language-independent description language.
The RTI Code Generator accepts input in the following formats:
- OMG IDL. This format is a standardized component of the DDS specification. It describes data types with a C++-like syntax. A link to the latest specification can be found here: https://www.omg.org/spec/IDL.
- XML in a DDS-specific format. This XML format is terser, and therefore easier to read and write by hand, than an XSD file. It offers the general benefits of XML-extensibility and ease of integration, while fully supporting DDS-specific data types and concepts. A link to the latest specification, including a description of the XML format, can be found here: https://www.omg.org/spec/DDS-XTypes/.
- XSD format. You can describe data types with XML schemas (XSD). A link to the latest specification, including a description of the XSD format, can be found here: https://www.omg.org/spec/DDS-XTypes/.
Define a type programmatically at run time.
This method may be appropriate for applications with dynamic data description needs: applications for which types change frequently or cannot be known ahead of time.
Register your type with a logical name.
Create a Topic using the type name you previously registered.
If you’ve chosen to use a built-in type instead of defining your own, you will use the API constant corresponding to that type’s name.
Create one or more DataWriters to publish your data and one or more DataReaders to subscribe to it.
The concrete types of these objects depend on the concrete data type you’ve selected, in order to provide you with a measure of type safety.
Whether publishing or subscribing to data, you will need to know how to create and delete DDS data samples and how to get and set their fields. These tasks are described in the section on Working with DDS Data Samples in the RTI Connext DDS Core Libraries User’s Manual (available here if you have Internet access).
4.1.1. Introduction to the Type System¶
A user data type is any custom type that your application defines for use with RTI Connext DDS Micro. It may be a structure, a union, a value type, an enumeration, or a typedef (or language equivalents).
Your application can have any number of user data types. They can be composed of any of the primitive data types listed below or of other user data types.
Only structures, unions, and value types may be read and written directly by Connext DDS Micro; enums, typedefs, and primitive types must be contained within a structure, union, or value type. In order for a DataReader and DataWriter to communicate with each other, the data types associated with their respective Topic definitions must be identical.
- octet, char, wchar
- short, unsigned short
- long, unsigned long
- long long, unsigned long long
- float
- double, long double
- boolean
- enum (with or without explicit values)
- bounded string and wstring
The following type-building constructs are also supported:
- module (also called a package or namespace)
- pointer
- array of primitive or user type elements
- bounded sequence of elements—a sequence is a variable-length ordered collection, such as a vector or list
- typedef
- union
- struct
- value type, a complex type that supports inheritance and other object-oriented features
To use a data type with Connext DDS Micro, you must define that type in a way the middleware understands and then register the type with the middleware. These steps allow Connext DDS Micro to serialize, deserialize, and otherwise operate on specific types. They will be described in detail in the following sections.
4.1.1.1. Sequences¶
A sequence contains an ordered collection of elements that are all of the same type. The operations supported in the sequence are documented in the C API Reference and C++ API Reference HTML documentation.
Elements in a sequence are accessed with their index, just like elements in an array. Indices start at zero in all APIs. Unlike arrays, however, sequences can grow in size. A sequence has two sizes associated with it: a physical size (the “maximum”) and a logical size (the “length”). The physical size indicates how many elements are currently allocated by the sequence to hold; the logical size indicates how many valid elements the sequence actually holds. The length can vary from zero up to the maximum. Elements cannot be accessed at indices beyond the current length.
A sequence must be declared as bounded. A sequence’s “bound” is the maximum number of elements that the sequence can contain at any one time. A finite bound is very important because it allows RTI Connext DDS Micro to preallocate buffers to hold serialized and deserialized samples of your types; these buffers are used when communicating with other nodes in your distributed system.
By default, any unbounded sequences found in an IDL file will be given a default bound of 100 elements. This default value can be overwritten using RTI Code Generator‘s -sequenceSize command-line argument (see the Command-Line Arguments chapter in the RTI Code Generator User’s Manual, available here if you have Internet access).
4.1.1.2. Strings and Wide Strings¶
Connext DDS Micro supports both strings consisting of single-byte characters (the IDL string type) and strings consisting of wide characters (IDL wstring). The wide characters supported by Connext DDS Micro are large enough to store 4-byte Unicode/UTF16 characters.
Like sequences, strings must be bounded. A string’s “bound” is its maximum length (not counting the trailing NULL character in C and C++).
In C and Traditional C++, strings are mapped to char*. Optionally, the mapping in Traditional C++ can be changed to std::string by generating code with the option -useStdString.
By default, any unbounded string found in an IDL file will be given a default bound of 255 elements. This default value can be overwritten using RTI Code Generator‘s -stringSize command-line argument (see the Command-Line Arguments chapter in the RTI Code Generator User’s Manual, available here if you have Internet access).
4.1.1.2.1. IDL String Encoding¶
The “Extensible and Dynamic Topic Types for DDS specification” (https://www.omg.org/spec/DDS-XTypes/) standardizes the default encoding for strings to UTF-8. This encoding shall be used as the wire format. Language bindings may use the representation that is most natural in that particular language. If this representation is different from UTF-8, the language binding shall manage the transformation to/from the UTF-8 wire representation.
As an extension, Connext DDS Micro offers ISO_8859_1 as an alternative string wire encoding.
This section describes the encoding for IDL strings across different languages in Connext DDS Micro and how to configure that encoding.
C, Traditional C++
IDL strings are mapped to a NULL-terminated array of DDS_Char (char*). Users are responsible for using the right character encoding (UTF-8 or ISO_8859_1) when populating the string values. This applies to all generated code, DynamicData, and Built-in data types. The middleware does not transform from the language binding encoding to the wire encoding.
4.1.1.2.2. IDL Wide Strings Encoding¶
The “Extensible and Dynamic Topic Types for DDS specification” (https://www.omg.org/spec/DDS-XTypes/) standardizes the default encoding for wide strings to UTF-32. This encoding shall be used as the wire format.
Wide-string characters have a size of 4 bytes on the wire with UTF-32 encoding.
Language bindings may use the representation that is most natural in that particular language. If this representation is different from UTF-32, the language binding shall manage the transformation to/from the UTF-32 wire representation.
C, Traditional C++
IDL wide strings are mapped to a NULL-terminated array of DDS_Wchar (DDS_Wchar*). DDS_WChar is an unsigned 4-byte integer. Users are responsible for using the right character encoding (UTF-32) when populating the wide-string values. This applies to all generated code, DynamicData, and Built-in data types. Connext DDS Micro does not transform from the language binding encoding to the wire encoding.
4.1.1.2.3. Sending Type Information on the Network¶
Connext DDS Micro can send type information the network using a concept called type objects. A type objects is a description of a type suitable to network transmission, and is commonly used by for example tools to visualize data from any application.
However, please note that Connext DDS Micro does not support sending type information on the network. Instead, tools can load type information from XML files generated from IDL using rtiddsgen. Please refer to the RTI Code Generator’s User’s Manual for more information (available here if you have Internet access).
4.1.2. Creating User Data Types with IDL¶
You can create user data types in a text file using IDL (Interface Description Language). IDL is programming-language independent, so the same file can be used to generate code in C and Traditional C++. RTI Code Generator parses the IDL file and automatically generates all the necessary routines and wrapper functions to bind the types for use by Connext DDS Micro at run time. You will end up with a set of required routines and structures that your application and Connext DDS Micro will use to manipulate the data.
Please refer to the section on Creating User Data Types with IDL in the RTI Connext DDS Core Libraries User’s Manual for more information (available here if you have Internet access).
Note: Not all features in RTI Code Generator are supported when generating code for Connext DDS Micro, see Unsupported Features of rtiddsgen with Connext DDS Micro.
4.1.3. Working with DDS Data Samples¶
You should now understand how to define and work with data types. Now that you have chosen one or more data types to work with, this section will help you understand how to create and manipulate objects of those types.
In C:
You create and delete your own objects from factories, just as you create Connext DDS Micro objects from factories. In the case of user data types, the factory is a singleton object called the type support. Objects allocated from these factories are deeply allocated and fully initialized.
/* In the generated header file: */
struct MyData {
char* myString;
};
/* In your code: */
MyData* sample = MyDataTypeSupport_create_data();
char* str = sample->myString; /*empty, non-NULL string*/
/* ... */
MyDataTypeSupport_delete_data(sample);
In Traditional C++:
Without the -constructor option, you create and delete objects using the TypeSupport factories.
MyData* sample = MyDataTypeSupport::create_data();
char* str = sample->myString; // empty, non-NULL string
// ...
MyDataTypeSupport::delete_data(sample);
Please refer to the section on Working with DDS Data Samples in the RTI Connext DDS Core Libraries User’s Manual for more information (available here if you have Internet access).