Typesupport

The TypeSupport is a crucial component in DDS which provides an interface for handling data types in a standardized way. It is responsible for managing the serialization, deserialization, and type-specific operations for user-defined data types in a DDS system.

In this sense, the TypeSupport ensures the seamless exchange of data between DataReader and DataWriter by providing a common data representation.

A Safe DDS type support is any class that implements the following interfaces:

  • datacentric::ITypeSupport<T>: Defines the type support specific type and its serialization and deserialization methods.

  • dds::TypeSupport: To register the type support within the DDS system and handle the type key identifier.

Data representation

For the purpose of achieving interoperability with other DDS vendors, it is necessary that the implementation of any Safe DDS TypeSupport adheres to a commonly accepted serialization and deserialization format known as CDR (Common Data Representation). To facilitate CDR-compliant serialization and deserialization functionality, the following classes are provided:

To set and decode the type of data representation used, the following structure is provided:

By leveraging these classes, the Safe DDS TypeSupport implementation ensures compatibility with other DDS vendors, promoting effective communication and data exchange across various DDS systems.

Registering a type

For a DDS TypeSupport to be utilized effectively, it is necessary to register it within a the application DomainParticipant. Once this registration process is completed, it is possible to leverage the safety-typed APIs offered by TypedDataReader and TypedDataWriter.

In order to register a dds::TypeSupport interface into a dds::DomainParticipant, the TypeSupport::register_type method shall be used.

// Register the type
CustomTypeSupport type_support{};
type_support.register_type(*participant, type_name);

Safe DDS-Gen

Safe DDS-Gen is a Java application that generates Safe DDS source code using the data types defined in an IDL (Interface Definition Language) file. This generated source code can be used in any Safe DDS application in order to define the data type of a topic, which will later be used to publish or subscribe. Please refer to Typesupport for more information on data types.

To declare the structured data, the IDL format must be used. IDL is a specification language, made by OMG (Object Management Group), which describes an interface in a language independent manner, allowing communication between software components that do not share the same language. The Safe DDS-Gen tool reads the IDL files and parses a subset of the OMG IDL specification to generate source code for data serialization, this subset is detailed in the Supported IDL types section.

Safe DDS-Gen generated source code uses provided CDR serialization implementations detailed on Data representation. Therefore, as stated in the RTPS standard, when the data are sent, the serialization is performed using the corresponding Common Data Representation (CDR).

The main feature of Safe DDS-Gen is to facilitate the implementation of DDS applications without the knowledge of serialization or deserialization mechanisms.

For installing Safe DDS-Gen, please refer to Installation.

HelloWorld TypeSupport

For reference, this documentation provides a simple example of a TypeSupport implementation for the HelloWorld data type used in the getting started section:

// Data Type used by HelloWorldTypeSupport
struct HelloWorld
{
    uint32_t index;
    uint32_t message_size;
    char message[255];
};

// TypeSupport for HelloWorld
class HelloWorldTypeSupport : public datacentric::ITypeSupport<HelloWorld>,
    public dds::TypeSupport
{
public:

    // Definition of internal data type
    using DataType = HelloWorld;

    // Implementation of dds::TypeSupport
    dds::ReturnCode register_type(
            dds::DomainParticipant& participant,
            const memory::IStringView& type_name) noexcept override
    {
        return participant.register_type(*this, type_name);
    }

    const memory::IStringView& get_type_name() const noexcept override
    {
        static constexpr char const* default_name = "HelloWorldTypeSupport";
        static const memory::container::StaticString256 typesupport_name(default_name);
        return typesupport_name;
    }

    bool has_keys() const noexcept override
    {
        return false;
    }

    protocol::KeyHash compute_key_hash(
            const memory::IConstByteArrayView& /* view */,
            const protocol::PayloadKind& /* kind */) const noexcept override
    {
        return datacentric::KeyHash{};
    }

    size_t max_serialized_size() const noexcept override
    {
        return
            sizeof(serialization::RepresentationHeader) +
            sizeof(HelloWorld::index) +
            sizeof(HelloWorld::message_size) +
            sizeof(HelloWorld::message);
    }

    // Implementation of datacentric::ITypeSupport
    size_t serialized_size(
            const DataType& sample) const noexcept override
    {
        return
            sizeof(serialization::RepresentationHeader) +
            sizeof(HelloWorld::index) +
            sizeof(HelloWorld::message_size) +
            sample.message_size;
    }

    safedds::ReturnCode serialize(
            const DataType& sample,
            memory::IByteArrayView& buffer) const noexcept override
    {
        // Do not serialize if size is bigger than buffer capacity
        if (buffer.size() < serialized_size(sample))
        {
            return safedds::ReturnCode::SERIALIZATION_INVALID_BUFFER_LENGTH;
        }

        /***************************************************
        * RETURN CODE SAFETY CHECKS ARE OMITTED FOR CLARITY
        ***************************************************/

        // Create a CDR serializer on the provided buffer
        serialization::cdr::Serializer ser(buffer);

        // Serialize a RepresentationHeader
        serialization::RepresentationHeader representation_header{};
        representation_header.init_as_cdr();
        ser.serialize_array(
            representation_header.encapsulation_kind_.data(),
            serialization::ENCAPSULATION_KIND_SIZE);
        ser.serialize_array(
            representation_header.encapsulation_options_.data(),
            serialization::ENCAPSULATION_OPTIONS_SIZE);

        // Serialize the data type
        ser.serialize(sample.index);
        ser.serialize(sample.message_size);
        ser.serialize_array(sample.message, sample.message_size);

        return safedds::ReturnCode::OK;
    }

    safedds::ReturnCode deserialize(
            const memory::IConstByteArrayView& buffer,
            DataType& sample) const noexcept override
    {
        /***************************************************
        * RETURN CODE SAFETY CHECKS ARE OMITTED FOR CLARITY
        ***************************************************/

        // Create a CDR deserializer  for the representation header on native endianness
        serialization::cdr::Deserializer representation_header_deser(buffer, true);

        // Deserialize RepresentationHeader
        serialization::RepresentationHeader representation_header{};
        protocol::rtps::RTPSTypeDeserializer::deserialize(
            representation_header_deser,
            representation_header);

        // Retrieve the payload buffer
        memory::byte_array::ByteArrayView payload_view = {nullptr, 0};
        representation_header_deser.deserializer_skip(
            representation_header_deser.deserializer_remaining_size(),
            payload_view);

        // Create a deserializer for the payload
        serialization::cdr::Deserializer deserializer(
            payload_view,
            representation_header.check_native_endianness());

        // Deserialize the data type
        deserializer.deserialize(sample.index);
        deserializer.deserialize(sample.message_size);
        deserializer.deserialize_array(sample.message, sample.message_size);

        return safedds::ReturnCode::OK;
    }

    protocol::KeyHash get_key(
            const DataType& /* data */) const noexcept override
    {
        return datacentric::KeyHash{};
    }

    datacentric::KeyHash get_key(
            const memory::IConstByteArrayView& /* view */,
            const protocol::PayloadKind& /* kind */) const noexcept override
    {
        return datacentric::KeyHash{};
    }

};