3.5.6. Definition of data types

The definition of the data type exchanged in a Topic is divided in two classes: the TypeSupport and the TopicDataType.

TopicDataType describes the data type exchanged between a publication and a subscription, i.e., the data corresponding to a Topic. The user has to create a specialized class for each specific type that will be used by the application.

Any specialization of TopicDataType must be registered in the DomainParticipant before it can be used to create Topic objects. A TypeSupport object encapsulates an instance of TopicDataType, providing the functions needed to register the type and interact with the publication and subscription. To register the data type, create a new TypeSupport with a TopicDataType instance and use the register_type() member function on the TypeSupport. Then the Topic can be created with the registered type name.

Note

Registering two different data types on the same DomainParticipant with identical names is not allowed and will issue an error. However, it is allowed to register the same data type within the same DomainParticipant, with the same or different names. If the same data type is registered twice on the same DomainParticipant with the same name, the second registering will have no effect, but will not issue any error.

// Create a DomainParticipant in the desired domain
DomainParticipant* participant =
        DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT);
if (nullptr == participant)
{
    // Error
    return;
}

// Register the data type in the DomainParticipant.
// If nullptr is used as name argument, the one returned by the type itself is used
TypeSupport custom_type_support(new CustomDataType());
custom_type_support.register_type(participant, nullptr);

// The previous instruction is equivalent to the following one
// Even if we are registering the same data type with the same name twice, no error will be issued
custom_type_support.register_type(participant, custom_type_support.get_type_name());

// Create a Topic with the registered type.
Topic* topic =
        participant->create_topic("topic_name", custom_type_support.get_type_name(), TOPIC_QOS_DEFAULT);
if (nullptr == topic)
{
    // Error
    return;
}

// Create an alias for the same data type using a different name.
custom_type_support.register_type(participant, "data_type_name");

// We can now use the aliased name to If no name is given, it uses the name returned by the type itself
Topic* another_topic =
        participant->create_topic("other_topic_name", "data_type_name", TOPIC_QOS_DEFAULT);
if (nullptr == another_topic)
{
    // Error
    return;
}

3.5.6.1. Dynamic data types

Instead of directly writing the specialized TopicDataType class, it is possible to dynamically define data types following the OMG Extensible and Dynamic Topic Types for DDS interface. Data types can also be described on an XML file that is dynamically loaded.

// Create a DomainParticipant in the desired domain
DomainParticipant* participant =
        DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT);
if (nullptr == participant)
{
    // Error
    return;
}

// Load the XML file with the type description
DomainParticipantFactory::get_instance()->load_XML_profiles_file("example_type.xml");

// Retrieve the an instance of the desired type
DynamicTypeBuilder::_ref_type dyn_type_builder;
DomainParticipantFactory::get_instance()->get_dynamic_type_builder_from_xml_by_name("DynamicType",
        dyn_type_builder);

// Register dynamic type
TypeSupport dyn_type_support(new DynamicPubSubType(dyn_type_builder->build()));
dyn_type_support.register_type(participant, nullptr);

// Create a Topic with the registered type.
Topic* topic =
        participant->create_topic("topic_name", dyn_type_support.get_type_name(), TOPIC_QOS_DEFAULT);
if (nullptr == topic)
{
    // Error
    return;
}

A complete description of the dynamic definition of types can be found on the XTypes section.

3.5.6.2. Data types with a key

Data types that define a set of fields to form a unique key can distinguish different data sets within the same data type.

To define a keyed Topic, the getKey() member function on the TopicDataType has to be overridden to return the appropriate key value according to the data fields. Additionally, the m_isGetKeyDefined data member needs to be set to true to let the entities know that this is a keyed Topic and that getKey() should be used. Types that do not define a key will have m_isGetKeyDefined set to false.

There are three ways to implement keys on the TopicDataType:

  • Adding a @Key annotation to the members that form the key in the IDL file when using Fast DDS-Gen.

  • Adding the attribute Key to the member and its parents when using XTypes.

  • Manually implementing the getKey() member function on the TopicDataType and setting the m_isGetKeyDefined data member value to true.

Data types with key are used to define data sub flows on a single Topic. Data values with the same key on the same Topic represent data from the same sub-flow, while data values with different keys on the same Topic represent data from different sub-flows. The middleware keeps these sub-flows separated, but all will be restricted to the same QoS values of the Topic. If no key is provided, the data set associated with the Topic is restricted to a single flow.

3.5.6.3. Type support context

Fast DDS allows passing a user-defined context object to the type support callbacks, enabling type-specific information (e.g., upper bounds for strings and sequences) to be available during serialization, deserialization, and related operations without needing global state.

The context is represented by the TopicDataType::Context interface. Users subclass it to carry whatever per-endpoint information their type requires:

struct MyContext : eprosima::fastdds::dds::TopicDataType::Context
{
    uint32_t string_max_length = 256;
    uint32_t sequence_max_length = 1024;
};

The context-aware virtual methods of TopicDataType all follow the same pattern: they receive a const std::shared_ptr<TopicDataType::Context>& as their first argument, and their default implementation ignores the context and delegates to the corresponding context-free method. Users may override any subset of the following methods:

  • serialize_ctx() — serialize a sample using the context.

  • deserialize_ctx() — deserialize a payload using the context.

  • calculate_serialized_size_ctx() — compute the serialized size with the context.

  • create_data_ctx() — allocate a new sample using the context.

  • delete_data_ctx() — deallocate a sample using the context.

  • compute_key_ctx() — extract the key using the context.

  • is_bounded_ctx() — check whether the type is bounded with this context.

  • is_plain_ctx() — check whether the type is plain with this context.

  • construct_sample_ctx() — in-place construct a sample using the context.

  • get_max_serialized_size_ctx() — return the maximum serialized size with this context.

The context is attached to a DataWriter or DataReader before enabling the entity, using DataWriter::set_type_support_context() or DataReader::set_type_support_context() respectively. Because the context is set before the entity is enabled, it is guaranteed to be available for every write or read operation performed during the entity’s lifetime.