14.2. Dynamic Language Binding

The Dynamic Language Binding API allows to define data types at runtime instead of having the types predefined as it is required by the Plain Language Binding. This API includes both the type definition and, the getters and setters required to use the defined types. Type definition can also be done using a XML configuration file as explained in Dynamic Types profiles section.

This section presents first the Dynamic Language Binding API, and then the supported types and specific examples defining and using those types.

14.2.1. Dynamic Language Binding Interfaces

This section briefly presents the Dynamic Language Binding API. For more information, please refer both to the DDS-XTypes specification and the API reference.

14.2.1.1. TypeDescriptor

TypeDescriptor is in charge of describing the state of a type. Objects of this interface have value semantics allowing the TypeDescriptor data to be deeply copied and compared.

14.2.1.2. AnnotationDescriptor

AnnotationDescriptor is in charge of describing the user-defined applied annotation to a specific element. Objects of this interface have value semantics allowing the AnnotationDescriptor data to be deeply copied and compared.

14.2.1.3. MemberDescriptor

MemberDescriptor is in charge of describing the state of a specific member of a type. Objects of this interface have value semantics allowing the MemberDescriptor data to be deeply copied and compared.

14.2.1.4. VerbatimTextDescriptor

VerbatimTextDescriptor is in charge of describing the @verbatim builtin annotation application. Objects of this interface have value semantics allowing the VerbatimTextDescriptor data to be deeply copied and compared.

14.2.1.5. DynamicTypeBuilderFactory

The DynamicTypeBuilderFactory serves as a singleton which instance is responsible for both creating and deleting DynamicTypeBuilder objects. This class provides the generic DynamicTypeBuilderFactory::create_type API, and also specific APIs to define other basic types such as strings, sequences, etc. More information can be found in Supported Types section.

14.2.1.6. DynamicType

DynamicType objects represents a specific type definition. Once the DynamicType has been built, it cannot be modified. Objects of this interface have reference semantics, so the API receives a nil-reference which is then returned pointing to the correct DynamicType address.

14.2.1.7. DynamicTypeMember

DynamicTypeMember represents a data member of a DynamicType. Objects of this interface have reference semantics, so the API receives a nil-reference which is then returned pointing to the correct DynamicTypeMember address.

14.2.1.8. DynamicTypeBuilder

DynamicTypeBuilder interface allows the instantiation of concrete DynamicType objects and serves as a transitional state for configuring the DynamicType before its creation. Upon definition, DynamicTypeBuilderFactory leverages the information contained in the builder to create the DynamicType. DynamicTypeBuilder::build allows for creating the fully constructed DynamicType. Builders remain reusable after DynamicType creation, ensuring changes to the builder do not affect previously created types.

14.2.1.9. DynamicDataFactory

DynamicDataFactory serves as a singleton which instance is responsible for both creating and deleting DynamicData objects from a given DynamicType instance.

14.2.1.10. DynamicData

DynamicData represents a data instance of a DynamicType, providing functionalities to access and modify data values. Each DynamicData object corresponds to an object of the type represented by its DynamicType. Offering reflective getters and setters, DynamicData enables manipulation of individual data samples.

14.2.2. Supported Types

This section describes the supported Type System including examples of how to instantiate those specific types using the Dynamic Language Binding API and the XML configuration file. The C++ examples also include instantiating the corresponding DynamicData sample, and setting and reading a value.

14.2.2.1. Primitive Types

Primitive types are self-describing and can be created without configuration parameters. The DynamicTypeBuilderFactory interface exposes the method DynamicTypeBuilderFactory::get_primitive_type to allow users to directly get the corresponding primitive DynamicType. The DynamicData class provides specific getters and setters for each primitive data type.

The following table shows the supported primitive types and their corresponding TypeKind. The TypeKind is used to query the DynamicTypeBuilderFactory for the specific primitive DynamicType.

C++ Type

TypeKind

bool

TK_BOOLEAN

char

TK_CHAR8

wchar_t

TK_CHAR16

uint8_t

TK_BYTE / TK_UINT8

int8_t

TK_INT8

int16_t

TK_INT16

uint16_t

TK_UINT16

int32_t

TK_INT32

uint32_t

TK_UINT32

int64_t

TK_INT64

uint64_t

TK_UINT64

float

TK_FLOAT32

double

TK_FLOAT64

long double

TK_FLOAT128

The example below shows how to create an structure with primitive members.

struct PrimitivesStruct
{
    boolean my_bool;
    octet my_octet;
    char my_char;
    wchar my_wchar;
    long my_long;
    unsigned long my_ulong;
    int8 my_int8;
    uint8 my_uint8;
    short my_short;
    unsigned short my_ushort;
    long long my_longlong;
    unsigned long long my_ulonglong;
    float my_float;
    double my_double;
    long double my_longdouble;
};

For a detailed explanation about the XML definition of this type, please refer to XML Primitive Types.

14.2.2.1.1. Type promotions

The Dynamic Language Binding also supports type promotion, enabling implicit promotion of types during both get() and set() operations. This means that a smaller type can be implicitly promoted to a larger type, but not the other way around.

The following promotions are supported:

TypeKind

Allowed promotions

TK_INT8

TK_INT16, TK_INT32, TK_INT64, TK_FLOAT32, TK_FLOAT64, TK_FLOAT128

TK_INT16

TK_INT32, TK_INT64, TK_FLOAT32, TK_FLOAT64, TK_FLOAT128

TK_INT32

TK_INT64, TK_FLOAT64, TK_FLOAT128

TK_INT64

TK_FLOAT128

TK_UINT8

TK_INT16, TK_INT32, TK_INT64, TK_UINT16, TK_UINT32, TK_UINT64, TK_FLOAT32, TK_FLOAT64, TK_FLOAT128

TK_UINT16

TK_INT32, TK_INT64, TK_UINT32, TK_UINT64, TK_FLOAT32, TK_FLOAT64, TK_FLOAT128

TK_UINT32

TK_INT64, TK_UINT64, TK_FLOAT64, TK_FLOAT128

TK_UINT64

TK_FLOAT128

TK_FLOAT32

TK_FLOAT64, TK_FLOAT128

TK_FLOAT64

TK_FLOAT128

TK_FLOAT128

(none)

TK_CHAR8

TK_CHAR16, TK_INT16, TK_INT32, TK_INT64, TK_FLOAT32, TK_FLOAT64, TK_FLOAT128

TK_CHAR16

TK_INT32, TK_INT64, TK_FLOAT32, TK_FLOAT64, TK_FLOAT128

TK_BYTE

(any)

TK_BOOLEAN

TK_INT8, TK_INT16, TK_INT32, TK_INT64, TK_UINT8, TK_UINT16, TK_UINT32, TK_UINT64, TK_FLOAT32, TK_FLOAT64, TK_FLOAT128

14.2.2.2. String Types

String types are one-dimensional collections of characters (TK_CHAR8 or TK_CHAR16. The latest are also known as wide strings or wstring). The TypeKinds used to identify string types are TK_STRING8 and TK_STRING16. The string may be bounded, setting a maximum length, or unbounded. This is configured using TypeDescriptor bound property.

DynamicTypeBuilderFactory exposes the functions DynamicTypeBuilderFactory::create_string_type and DynamicTypeBuilderFactory::create_wstring_type that eases string creation providing the corresponding maximum length parameter (LENGTH_UNLIMITED is used for unbounded strings).

DynamicData class provides also with specific getters and setters: DynamicData::get_string_value, DynamicData::get_wstring_value, DynamicData::set_string_value, and DynamicData::set_wstring_value.

struct StringsStruct
{
    string my_string;
    wstring my_wstring;
    string<41925> my_bounded_string;
    wstring<20925> my_bounded_wstring;
};

For a detailed explanation about the XML definition of this type, please refer to XML String Types.

14.2.2.3. Enumeration Types

An enumeration contains a set of supported values (enumeration literals) and a selected value among those supported. The TypeKind used to identify enumeration types is TK_ENUM.

The enumeration literals must be configured using the DynamicTypeBuilder by calling the DynamicTypeBuilder::add_member function for the respective supported values. The MemberDescriptor passed to the previous function must determine the enumeration literal name by using name property. The underlying primitive type related to the enumeration is configured using MemberDescriptor type property. This primitive type is determined when adding the first enumeration literal. For the enumeration type to be consistent, the remaining enumeration literals must be of the same primitive type. Additionally, the enumeration literal value might be set using MemberDescriptor default_value property. The behavior is the same as setting the @value builtin annotation.

As the enumeration type is basically a signed integer type which might take only some specific values defined with the enumeration literals, the corresponding DynamicData getters and setters are the ones corresponding to the underlying signed integer type (and any other method promotable to that specific primitive type).

enum MyEnum
{
    A,
    B,
    C
};

struct EnumStruct
{
    MyEnum my_enum;
};

For a detailed explanation about the XML definition of this type, please refer to XML Enumeration Types.

14.2.2.4. Bitmask Types

Bitmask types are a collection of boolean flags (bitflags) that can be set individually. The TypeKind used to identify bitmask types is TK_BITMASK. The bitmasks bound, maximum number of bits, must be set using the TypeDescriptor bound property. The maximum bound allowed is 64 bits.

The bitflags must be configured using the DynamicTypeBuilder by calling the DynamicTypeBuilder::add_member function. Each bitflag is described using a MemberDescriptor defining the bitflag name using the name property. The underlying primitive type related to bitflags must be of kind TK_BOOLEAN and must be set in MemberDescriptor type property. The MemberDescriptor id property might be used to indicate the bitflag position within the bitmask. This behavior is the same as setting the @position builtin annotation. If the position is not specified, sequential order is followed.

The DynamicTypeBuilderFactory exposes the function DynamicTypeBuilderFactory::create_bitmask_type to facilitate the creation of bitmask types.

Bitmask types can be manipulated either using the DynamicData::get_boolean_value/DynamicData::set_boolean_value in order to set a specific bitflag, or by using the unsigned integer setter/getter corresponding to the bitmask bound. In this latest case, only bitflags are going to be set (bits not named are always unset).

@bit_bound(8)
bitmask MyBitMask
{
    @position(0) flag0,
    flag1,
    flag2,
    @position(5) flag5
};

struct BitmaskStruct
{
    MyBitMask my_bitmask;
};

For a detailed explanation about the XML definition of this type, please refer to XML Bitmask Types.

14.2.2.5. Alias Types

Alias types provide an alternative name to an already existing type. The TypeKind used to identify aliases is TK_ALIAS. Besides defining the alias name, the underlying type must be set using TypeDescriptor base_type property. Alias recursion is supported by defining another alias type as the base type.

Once the DynamicData is created, information can be accessed as if working with the base type.

typedef MyEnum MyAliasedEnum;
typedef string<100> MyAliasedBoundedString;
typedef MyAliasedEnum MyRecursiveAlias;

struct AliasStruct
{
    MyAliasedEnum my_aliased_enum;
    MyAliasedBoundedString my_aliased_bounded_string;
    MyRecursiveAlias my_recursive_alias;
};

For a detailed explanation about the XML definition of this type, please refer to XML Alias Types.

14.2.2.6. Sequence types

Sequence types are one-dimensional collections of any type. The TypeKind used to identify sequences is TK_SEQUENCE. TypeDescriptor element_type property must be set with the collection’s type. Additionally, bound property must also be configured with the sequence’s maximum length, or LENGTH_UNLIMITED in case of unbounded sequences.

DynamicTypeBuilderFactory exposes the function DynamicTypeBuilderFactory::create_sequence_type to facilitate the creation of this type. This API requires the type stored in the collection and the collection’s bound, using LENGTH_UNLIMITED in case of unbounded sequences.

DynamicData class provides specific get_values() and set_values() functions for each primitive type, allowing users to easily work with sequences of primitive types. Primitive type promotion is also applicable for these methods. For sequences of more complex types, please refer to Managing Complex Types Data.

If a specific range of values within the sequence are to be modified, passing the starting index to get_values() / set_values() would only manage data from that element forward until the length of the given input. Specific collection’s element can be also be modified using the get_value() / set_value() passing the index of the element to be modified.

struct SequenceStruct
{
    sequence<MyBitMask> bitmask_sequence;
    sequence<short, 5> short_sequence;
};

For a detailed explanation about the XML definition of this type, please refer to XML Sequence Types.

14.2.2.7. Array types

Array types are multi-dimensional collections of any type. The TypeKind used to identify arrays is TK_ARRAY. TypeDescriptor element_type property must be set with the collection’s type. Additionally, bound property must be configured with the sequence containing the size of each dimension. Bound sequence must have at least one dimension and it is not allowed for any dimension to have size 0.

DynamicTypeBuilderFactory exposes the function DynamicTypeBuilderFactory::create_array_type to facilitate the creation of this type. This API requires the type stored in the collection and the sequence with the collection’s dimensions.

DynamicData class provides specific get_values() and set_values() functions for each primitive type, allowing users to easily work with arrays of primitives types. For arrays of more complex types, please refer to Managing Complex Types Data.

Note

Multi-dimensional arrays flatten every dimension into a single-dimension array.

Primitive type promotion is also applicable for these methods.

If a specific range of values within the array are to be modified, passing the starting index to get_values() / set_values() would only manage data from that element forward until the length of the given input. Specific collection’s element can be also be modified using the get_value() / set_value() passing the index of the element to be modified.

struct ArrayStruct
{
    long long_array[2][3][4];
};

For a detailed explanation about the XML definition of this type, please refer to XML Array Types.

14.2.2.8. Map Types

Map types are a collection of key/value pair types. Access to the value element is done through the key which is unique within the map type. The TypeKind used to identify maps is TK_MAP. TypeDescriptor element_type property must be set with the map value type. TypeDescriptor key_type property must be set with the map key type. Allowed key types are signed and unsigned integer types and string types.

Note

Currently, wide string keys are not supported as map keys.

Additionally, bound property must also be configured with the map’s maximum length, or LENGTH_UNLIMITED in case of unbounded maps.

DynamicTypeBuilderFactory exposes the DynamicTypeBuilderFactory::create_map_type function to facilitate the creation of this type. This API requires the type of both the key and the value stored in the collection, and the collection’s bound, using LENGTH_UNLIMITED in case of unbounded maps.

Manipulating map types data is more complex. First the MemberId corresponding to a specific key must be retrieved using DynamicData::get_member_id_by_name API. This API either returns the MemberId corresponding to the existing key or, if the key does not exist yet, it creates the key and returns the memberId associated to the just created key. In order to call this method, the correct string representation of the key value must be passed. The map value can now be set using the API corresponding to the map value type. For complex map values, please refer to Managing Complex Types Data.

struct MapStruct
{
    map<string, MyAliasedBoundedString> string_alias_unbounded_map;
    map<short, long, 2> short_long_map;
};

For a detailed explanation about the XML definition of this type, please refer to XML Map Types.

14.2.2.9. Structure Types

Structure types are an aggregation of members of different types. The TypeKind used to identify structures is TK_STRUCTURE. Structure types have single inheritance support, so a structure type might extend one other already defined structure. The structure type which is extended should be configured in the TypeDescriptor base_type property. Structure extensibility might be configured using TypeDescriptor extensibility_kind property.

Note

Currently, @nested builtin annotation is not supported.

Structure members must be configured using the DynamicTypeBuilder by calling the DynamicTypeBuilder::add_member function with the corresponding MemberDescriptor.

Note

Empty structures, with no members, are allowed.

Member name is configured using MemberDescriptor name property and the member type is set using type property. Structure members might be keyed to create topic instances by setting the MemberDescriptor is_key property. The behavior is the same as setting the @key builtin annotation. Additionally, MemberDescriptor default_value property might be set to configure the member default value, and MemberDescriptor id property sets explicitly the member ID. This behavior is the same as setting the @default and @id builtin annotations.

Note

Currently, Fast DDS-Gen does not support @default builtin annotation.

Note

Currently, Dynamic Language Binding API implementation does not support the following builtin annotations:

  • @optional

  • @must_understand

  • @external

  • @try_construct

Member data can be managed using the corresponding accessors for the underlying member type. Member ID might be retrieved using DynamicData::get_member_id_by_name API. For managing complex type members, please refer to Managing Complex Types Data.

struct InnerStruct
{
    @id(0x10) long first;
};

struct ParentStruct
{
    float first;
    long long second;
};

struct ComplexStruct : ParentStruct
{
    InnerStruct complex_member;
};

For a detailed explanation about the XML definition of this type, please refer to XML Structure Types.

14.2.2.10. Union Types

Union types are a special type of structure type where only one member exists. The TypeKind used to identify unions is TK_UNION. Member selection is performed by setting another special member called discriminator. The discriminator type must be defined using TypeDescriptor discriminator_type property. Supported discriminator TypeKind are the following:

  • TK_BOOLEAN

  • TK_BYTE

  • TK_CHAR8

  • TK_CHAR16

  • TK_INT8

  • TK_UINT8

  • TK_INT16

  • TK_UINT16

  • TK_INT32

  • TK_UINT32

  • TK_INT64

  • TK_UINT64

  • TK_ENUM

  • TK_ALIAS that resolves, directly or indirectly to one of the aforementioned types.

Union extensibility might be configured using TypeDescriptor extensibility_kind property.

Note

Currently, @nested builtin annotation is not supported.

Union members must be configured using the DynamicTypeBuilder by calling the DynamicTypeBuilder::add_member function with the corresponding MemberDescriptor. At least one union member must be added to the union type. Union member name is configured using MemberDescriptor name property and the member type is set using type property. It is also mandatory to either set MemberDescriptor is_default_label property or configure the label property. This latest property indicates the discriminator values that select this specific member. If no labels are configured, then the flag indicating the member to be the default one, must be set. Only one union member must be configured as default.

Additionally, MemberDescriptor default_value property might be set to configure the member default value, and MemberDescriptor id property sets explicitly the member ID. This behavior is the same as setting the @default and @id builtin annotations.

Note

Currently, Fast DDS-Gen does not support @default builtin annotation.

Note

Currently, Dynamic Language Binding API implementation does not support the following builtin annotations:

  • @optional

  • @must_understand

  • @external

  • @try_construct

Member data can be managed using the corresponding accessors for the underlying member type. Setting a member automatically changes the discriminator value selecting the set member. When reading a member, the discriminator must be selecting the member being read. Member ID might be retrieved using DynamicData::get_member_id_by_name API. For managing complex type members, please refer to Managing Complex Types Data.

union InnerUnion switch (short)
{
    case 0:
        @id(0x10) PrimitivesStruct first;
    case 1:
    default:
        long long second;
};

union ComplexUnion switch (long)
{
    case 0:
    case 1:
        long third;
    default:
        InnerUnion fourth;
};

For a detailed explanation about the XML definition of this type, please refer to XML Union Types.

14.2.2.11. Bitset Types

Bitset types are an aggregation of bitfields. The TypeKind used to identify bitsets is TK_BITSET. bound property contains the sequence with the bitfield’s bitcount (number of bits). In order to be consistent, the length of the bound sequence must agree with the number of bitfields. Bitset types have single inheritance support, so a bitset type might extend one other already defined bitset. The bitset type which is extended should be configured in the TypeDescriptor base_type property.

Bitfields must be configured using the DynamicTypeBuilder by calling the DynamicTypeBuilder::add_member function with the corresponding MemberDescriptor. At least one bitfield is required for the bitset to be consistent. Bitfield name is configured using MemberDescriptor name property, and the bitfield initial bit position is set using MemberDescriptor id property.

Note

For derived bitsets, the first bitfield initial position must be after the bits defined by the parent bitset type.

A bitfield manages exclusively a set of bits, so no bitfield superposition is allowed. Additionally, MemberDescriptor type property might be set to configure an integer type to access bitfield data. If not set, the minimum unsigned integer type is used instead:

Number of bits

C++ holder type

1

bool

2-8

uint8_t

9-16

uint16_t

17-32

uint32_t

33-64

uint64_t

Each bitfield (or member) works like its primitive type with the only difference that the internal storage only modifies the involved bits instead of the full primitive value.

bitset ParentBitSet
{
    bitfield<3> a;
    bitfield<1> b;
    bitfield<4>;
    bitfield<10> c;
    bitfield<12, short> d;
};

bitset ChildBitSet : ParentBitSet
{
    bitfield<1> e;
    bitfield<20, unsigned long> f;
};

struct BitsetStruct
{
   ChildBitSet my_bitset;
};

For a detailed explanation about the XML definition of this type, please refer to XML Bitset Types.

14.2.2.12. Annotations

14.2.2.12.1. Custom annotations

Both types and type members might be annotated using DynamicTypeBuilder::apply_annotation and DynamicTypeBuilder::apply_annotation_to_member API respectively.

Annotations are defined using an AnnotationDescriptor which provides two properties: type and value. The annotation type must be the DynamicType representing the annotation being applied. The TypeKind used to identify annotations is TK_ANNOTATION. The annotation name is set in TypeDescriptor name property.

The annotation type might have any number of parameters. Annotation parameters must be configured using the DynamicTypeBuilder by calling the DynamicTypeBuilder::add_member function with the corresponding MemberDescriptor. Annotation parameters must define the annotation parameter name in MemberDescriptor name property, and the parameter type using type property. Only the following types can be used to define an annotation parameter:

Note

Currently, wide string types are not supported as annotation parameters.

Annotation parameter values are defined with AnnotationDescriptor value property using AnnotationDescriptor::set_value(). The annotation parameter name provided to the API must coincide with the one defined with the annotation type. The annotation parameter value must be converted to its string representation.

Note

Currently, custom annotations are not supported with XML DynamicTypes.

@annotation MyAnnotation
{
    short length;
};

@MyAnnotation(length = 5)
struct AnnotatedStruct
{
    @MyAnnotation(length = 10) string string_var;
};

14.2.2.12.2. Builtin annotations

Beside the user-defined custom annotations, there are a number of builtin annotations that have already mentioned throughout this section.

The table below summarizes the builtin annotations that can be applied using the Dynamic Language Binding API. Please, refer to builtin annotations for the complete list and their behavior.

Builtin annotation

Dynamic Language Binding API

Dynamic Language Binding support

XML Dynamic Type profiles support

@appendable

TypeDescriptor extensibility_kind property.

@bit_bound

TypeDescriptor bound property for Bitset Types.
MemberDescriptor type property for Enumeration Types.

✅❌ (Enumeration types not configurable).

@default

MemberDescriptor default_value property.

default_literal

MemberDescriptor is_default_label property.

@extensibility

TypeDescriptor extensibility_kind property.

@external

MemberDescriptor is_shared property.

@final

TypeDescriptor extensibility_kind property.

@id

MemberDescriptor id property.

@key / @Key

MemberDescriptor is_key property.

@mutable

TypeDescriptor extensibility_kind property.

@nested

TypeDescriptor is_nested property.

@optional

MemberDescriptor is_optional property.

@position

MemberDescriptor id property.

@try_construct

MemberDescriptor try_construct_kind property.

@value

MemberDescriptor default_value property.

@verbatim

VerbatimTextDescriptor

14.2.2.13. Managing Complex Types Data

Some DynamicData instances manage complex types that cannot be directly modified with the primitive getters and setters. Dynamic Language Binding provides two possible approaches for managing complex data types:

  1. DynamicData::get_complex_value and DynamicData::set_complex_value: This API allows to get/set generic DynamicData. The main difference with the next approach is that this API performs always a copy.

  2. DynamicData::loan_value: this API allows to loan a reference to a DynamicData to work with preventing the data copy. DynamicData::return_loaned_value must be called to return the loan. Calling DynamicData::loan_value for an already loaned value will fail.

The following snippet includes an example of managing complex data using the same structure as the one defined in Structure types:

        // Create dynamic data based on the struct type
        DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(complexstruct_type)};

        // Get/Set complex API (copy)
        DynamicData::_ref_type complex_data;
        data->get_complex_value(complex_data, data->get_member_id_by_name("complex_member"));
        // Set data
        int32_t in_value {10};
        complex_data->set_int32_value(complex_data->get_member_id_by_name("first"), in_value);
        data->set_complex_value(data->get_member_id_by_name("complex_member"), complex_data);

        // Loan API
        DynamicData::_ref_type loan_data = data->loan_value(data->get_member_id_by_name("complex_member"));
        loan_data->set_int32_value(loan_data->get_member_id_by_name("first"), in_value);
        int32_t out_value;
        loan_data->get_int32_value(out_value, loan_data->get_member_id_by_name("first"));
        data->return_loaned_value(loan_data);