5.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 Micro data samples sent on the same Connext 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 Micro, you will carry out the following steps:

  1. 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.

  2. Register your type with a logical name.

  3. 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.

  4. 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).

5.1.1. Introduction to the Type System

A user data type is any custom type that your application defines for use with RTI Connext 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 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 Micro, you must define that type in a way the middleware understands and then register the type with the middleware. These steps allow Connext Micro to serialize, deserialize, and otherwise operate on specific types. They will be described in detail in the following sections.

5.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 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).

5.1.1.2. Strings and Wide Strings

Connext 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 Micro are large enough to store two-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*.

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).

5.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 Micro offers ISO_8859_1 as an alternative string wire encoding.

This section describes the encoding for IDL strings across different languages in Connext 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.

5.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-16. This encoding shall be used as the wire format.

When the data representation is Extended CDR version 1, wide-string characters have a size of 4 bytes on the wire with UTF-16 encoding. When the data representation is Extended CDR version 2, wide-string characters have a size of 2 bytes on the wire with UTF-16 encoding.

Language bindings may use the representation that is most natural in that particular language. If this representation is different from UTF-16, the language binding shall manage the transformation to/from the UTF-16 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 2-byte integer. Users are responsible for using the right character encoding (UTF-16) when populating the wide-string values. This applies to all generated code, DynamicData, and Built-in data types. Connext Micro does not transform from the language binding encoding to the wire encoding.

5.1.1.2.3. Sending Type Information on the Network

Connext Professional can send type information on the network using a concept called type objects. A type object is a description of a type suitable to network transmission, and is commonly used by tools to visualize data from any application.

However, please note that Connext 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 here for more information.

5.1.1.3. Extensible Types (X-Types) 1.2 Compatibility

Connext Micro supports the “Extensible and Dynamic Topic Types for DDS” (DDS-XTypes) specification from the Object Management Group (OMG), version 1.2 (https://www.omg.org/spec/DDS-XTypes/1.2) with the following limitations:

  • Extended Common Data Representation (CDR) encoding version 1 (XCDR) and Extended CDR encoding version 2 (XCDR2) are supported by default.

  • If RTI Code Generator (rtiddsgen) is used with the option -interpreted 0, support for X-Types is disabled and only plain CDR is supported (CDRv1 final types).

  • Connext Micro does not send type information.

  • Connext Micro does not perform type-compatibility checking based on the type information, only the type-name. This means that advanced X-Types 1.2 features cannot be supported, such as:

    • Type equivalence

    • String-length matching and truncation

    • Sequence-length matching and truncation

5.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 Micro at run time. You will end up with a set of required routines and structures that your application and Connext 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 Micro, see Unsupported Features of rtiddsgen with Connext Micro.

5.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 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).