Fast DDS入门九、Fast DDS的动态类型(Dynamic Types)详解

Fast DDS提供了定义和使用主题类型和主题数据的动态方式。实现了遵循用于DDS接口的OMG可扩展和动态主题类型。动态类的定义符合DDS XTypes V1.2的规范。

动态类型提供了在RTPS上工作的可能性,而不受与IDL相关的限制。使用它们,用户可以声明他们需要的不同类型并直接管理信息,从而避免了更新IDL文件和生成C++类的额外步骤。

为了为定义的动态类型提供最大的灵活性和功能,Fast DDS支持多种成员类型,从简单原语到嵌套结构。

一、基本动态类型

1、Primitive Types(对应C++的Primitive类型)

BOOLEAN

INT64

BYTE

UINT16

CHAR8

UINT32

CHAR16

UINT64

INT16

FLOAT32

INT32

FLOAT64

FLOAT128

根据定义,primitive类型是自我描述的,可以在没有配置参数的情况下创建。因此,DynamicTypeBuilderFactory公开了几个函数,允许用户创建动态类型,而无需执行DynamicTypeBuilder步骤。DynamicTypeBuilder仍然可以用于创建基元类型的动态数据,如下面的示例所示。DynamicData类为列表的每个基元类型都有一个特定的get()和set()函数。

// UsingBuilders
DynamicTypeBuilder_ptr created_builder = DynamicTypeBuilderFactory::get_instance()->create_int32_builder();
DynamicType_ptr created_type = DynamicTypeBuilderFactory::get_instance()->create_type(created_builder.get());
DynamicData* data = DynamicDataFactory::get_instance()->create_data(created_type);
data->set_int32_value(1);
// Creatingdirectly the Dynamic Type
DynamicType_ptr pType = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicData* data2 = DynamicDataFactory::get_instance()->create_data(pType);
data2->set_int32_value(1);

2、String和WString

字符串与原始类型非常相似,主要区别在于它们需要设置可以管理的缓冲区的大小。默认情况下,此大小设置为255个字符。

DynamicTypeBuilderFactory公开了函数create_string_type()和create_wstring_type(),以允许用户创建DynamicTypes,而无需DynamicTypeBuilder步骤。DynamicTypeBuilder仍然可以用于创建字符串类型的动态数据,如下面的示例所示。

// Using Builders
DynamicTypeBuilder_ptr created_builder = DynamicTypeBuilderFactory::get_instance()->create_string_builder(100);
DynamicType_ptr created_type = DynamicTypeBuilderFactory::get_instance()->create_type(created_builder.get());
DynamicData* data = DynamicDataFactory::get_instance()->create_data(created_type);
data->set_string_value("Dynamic String");
 
// Creating directly the Dynamic Type
DynamicType_ptr pType = DynamicTypeBuilderFactory::get_instance()->create_string_type(100);
DynamicData* data2 = DynamicDataFactory::get_instance()->create_data(pType);
data2->set_string_value("Dynamic String");

3、Alias(别名类型,类型绕来绕去,不建议使用)

别名类型提供了现有类型的替代名称。一旦创建了DynamicData,用户就可以像使用基类型一样访问其信息。

DynamicTypeBuilderFactory公开函数create_alias_type(),以允许用户创建alias类型,从而避免DynamicTypeBuilder步骤。DynamicTypeBuilder仍然可以用于创建Alias,如下例所示。

// Create the base type
DynamicTypeBuilder_ptr base_builder = DynamicTypeBuilderFactory::get_instance()->create_string_builder(100);
DynamicType_ptr base_type = DynamicTypeBuilderFactory::get_instance()->create_type(base_builder.get());
 
// Create alias using Builders
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_alias_builder(base_type,
                "alias");
DynamicData* data = DynamicDataFactory::get_instance()->create_data(builder.get());
data->set_string_value("Dynamic Alias String");
 
// Create alias type directly
DynamicType_ptr pAliasType = DynamicTypeBuilderFactory::get_instance()->create_alias_type(base_type, "alias");
DynamicData* data2 = DynamicDataFactory::get_instance()->create_data(pAliasType);
data2->set_string_value("Dynamic Alias String");

4、Enumeration(枚举类型)

枚举包含一组受支持的值和受支持值中的选定值。必须使用DynamicTypeBuilder配置支持的值,并为每个支持的值使用add_member()函数。此函数的输入是要添加的值的索引和名称。

DynamicData类具有get_enum_value()和set_enum_vvalue()函数,用于处理值索引或值名称字符串。

// Add enumeration values using the DynamicTypeBuilder
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_enum_builder();
builder->add_empty_member(0, "DEFAULT");
builder->add_empty_member(1, "FIRST");
builder->add_empty_member(2, "SECOND");
 
// Create the data instance
DynamicData* data = DynamicDataFactory::get_instance()->create_data(builder.get());
 
// Access value using the name
std::string sValue = "SECOND";
data->set_enum_value(sValue);
std::string sStoredValue;
data->get_enum_value(sStoredValue, MEMBER_ID_INVALID);
 
// Access value using the index
uint32_t uValue = 2;
data->set_enum_value(uValue);
uint32_t uStoredValue;
data->get_enum_value(uStoredValue, MEMBER_ID_INVALID);

5、BitMask(位掩码类型,对应std::bitset)

位掩码与枚举类型类似,但其成员作为位标志工作,可以单独打开和关闭。位操作可以在测试或设置位掩码值时应用。DynamicData具有特殊函数get_bitmask_value()和set_bitmask-value(),它们允许检索或修改完整值,而不是访问每个位。

位掩码可以绑定到多达64位的任意数量的位。

uint32_t limit = 5; // Stores as "octet"
 
// Add bitmask flags using the DynamicTypeBuilder
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_bitmask_builder(limit);
builder->add_empty_member(0, "FIRST");
builder->add_empty_member(1, "SECOND");
 
// Create the data instance
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(builder.get()));
 
// Access the mask values using the name
data->set_bool_value(true, "FIRST");                // Set the "FIRST" bit
bool bSecondValue = data->get_bool_value("SECOND"); // Get the "SECOND" bit
 
// Access the mask values using the index
data->set_bool_value(true, 1);                      // Set the "SECOND" bit
bool bFirstValue = data->get_bool_value(0);         // Get the "FIRST" bit
 
// Get the complete bitmask as integer
uint64_t fullValue;
data->get_bitmask_value(fullValue);

6、Structure

结构体类型是常见的复杂类型,它们允许在其中添加任何类型的成员。它们没有任何值,只用于包含其他类型。

为了管理结构内的类型,用户可以使用自己的id根据结构内类型的类型调用get()和set()函数。如果结构包含一个复杂的值,则应该与loan_value一起使用来访问它,并与return_loned_value一起使用来释放该指针。DynamicData管理借出值的计数器,如果不调用return_loned_value,则用户无法借出先前借出的值。

结构体成员的Id必须是连续的,从零开始,如果Id与下一个值不匹配,DynamicType将更改该Id。如果两个结构体成员具有相同的Id,则在添加第二个成员后,前一个成员将将其Id更改为下一个值。为了按名称获取成员的Id,DynamicData公开了get_member_Id_by_name()函数。

DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
builder->add_member(0, "first", DynamicTypeBuilderFactory::get_instance()->create_int32_type());
builder->add_member(1, "other", DynamicTypeBuilderFactory::get_instance()->create_uint64_type());
DynamicType_ptr struct_type(builder->build());
 
// Create the data instance
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(struct_type));
 
// Access struct members
data->set_int32_value(5, 0);
data->set_uint64_value(13, 1);

结构允许继承,完全具有相同的OOP含义。要从另一个结构继承,我们必须创建调用工厂的create_child_struct_builder()的结构。此函数与位集共享,并将根据父级的类型推断子类型。

DynamicTypeBuilder_ptr child_builder =
        DynamicTypeBuilderFactory::get_instance()->create_child_struct_builder(builder.get());

7、Bitset(不对应std::bitset,使用难度较高

位集类型与结构类型相似,但它们的成员仅是位字段,以最佳方式存储。在静态版本的位集中,每个位只使用内存中的一位(有平台限制),而不考虑对齐问题。位字段可以是匿名的(无法寻址),以跳过位集中未使用的位。

位集中的每个位字段都可以通过其所需的最小基元表示进行修改。

Number of bits

Primitive

1

BOOLEAN

2-8

UINT8

9-16

UINT16

17-32

UINT32

33-64

UINT64

每个位字段(或成员)的工作方式与它的primitive类型相同,唯一的区别是内部存储器只修改所涉及的位而不是完整的primitive值。

Bit_bound和位字段的位置可以使用注释设置(在静态和动态位集之间转换时很有用)。

// Create bitfields with the appropriate type for their size
DynamicTypeBuilder_ptr base_type_byte_builder =
        DynamicTypeBuilderFactory::get_instance()->create_byte_builder();
auto base_type_byte = base_type_byte_builder->build();
 
DynamicTypeBuilder_ptr base_type_uint32_builder =
        DynamicTypeBuilderFactory::get_instance()->create_uint32_builder();
auto base_type_uint32 = base_type_uint32_builder->build();
 
// Create the bitset with two bitfields
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_bitset_builder();
builder->add_member(0, "byte", base_type_byte);
builder->add_member(1, "uint32", base_type_uint32);
 
// Apply members' annotations
builder->apply_annotation_to_member(0, ANNOTATION_POSITION_ID, "value", "0");   // "byte" starts at position 0
builder->apply_annotation_to_member(0, ANNOTATION_BIT_BOUND_ID, "value", "2"); // "byte" is 2 bit length
builder->apply_annotation_to_member(1, ANNOTATION_POSITION_ID, "value", "10"); // "uint32" starts at position 10 (8 bits empty)
builder->apply_annotation_to_member(1, ANNOTATION_BIT_BOUND_ID, "value", "20"); // "uint32" is 20 bits length
 
// Create the data instance
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(builder.get()));
 
// Access values
data->set_byte_value(234, 0);
data->set_uint32_value(2340, 1);
octet bValue;
uint32_t uValue;
data->get_byte_value(bValue, 0);
data->get_uint32_value(uValue, 1);

位集类型允许继承,完全具有相同的OOP含义。要从另一个位集继承,必须创建调用工厂的create_child_struct_builder的位集。此函数与结构共享,并将根据父级的类型推断子类型。

DynamicTypeBuilder_ptr child_builder =
        DynamicTypeBuilderFactory::get_instance()->create_child_struct_builder(builder.get());

8、Union

联合类型一种特殊的结构,其中只有一个成员同时活跃。要控制这些成员,用户必须设置用于选择调用create_union_builder函数的当前成员的鉴别器类型(discriminator)。鉴别器本身是任何基元类型、字符串类型或联合类型的DynamicType。

要添加的每个成员都需要至少一个union_case_index来设置如何选择它,如果它是联合的默认值,也可以选择。

// Create the union DynamicTypeBuilder with an int32 discriminator
DynamicType_ptr discriminator = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_union_builder(discriminator);
 
// Add the union members. "firts" will be the default value
builder->add_member(0, "first", DynamicTypeBuilderFactory::get_instance()->create_int32_type(), "", { 0 },
        true);
builder->add_member(0, "second", DynamicTypeBuilderFactory::get_instance()->create_int64_type(), "", { 1 },
        false);
 
// Create the data instance
DynamicType_ptr union_type = builder->build();
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(union_type));
 
// Access the values using the member index
data->set_int32_value(9, 0);
data->set_int64_value(13, 1);
 
// Get the label of the currently selected member
uint64_t unionLabel;
data->get_union_label(unionLabel);

9、Sequence(对应std::vector)

一种复杂类型,将其成员作为项目列表进行管理,允许用户插入、删除或访问列表中的成员。要创建此类型,用户需要指定要存储的类型以及列表的大小限制(可选)。

为了简化这种类型的内存管理,DynamicData具有以下功能:

  • insert_sequence_data():在列表末尾创建一个新元素,并返回新元素的id。

  • remove_sequence_data():删除给定索引的元素并刷新id以保持列表的一致性。

  • clear_data():删除列表中的所有元素。

// Create a DynamicTypeBuilder for a sequence of two elements of type inte32
uint32_t length = 2;
DynamicType_ptr base_type = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicTypeBuilder_ptr builder =
        DynamicTypeBuilderFactory::get_instance()->create_sequence_builder(base_type, length);
 
// Create the data instance
DynamicType_ptr sequence_type = builder->build();
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(sequence_type));
 
// Insert and remove elements
MemberId newId, newId2;
data->insert_int32_value(10, newId);
data->insert_int32_value(12, newId2);
data->remove_sequence_data(newId);

10、Array(对应std::array)

数组与序列非常相似,有两个主要区别:它们可以有多个维度,不需要连续存储元素。

数组需要知道它所管理的维度的数量。为此,用户必须提供一个向量,该向量的元素数量与数组中的维度数量相同。向量中的每个元素表示给定维度的大小。如果元素的值设置为零,则应用默认值(100)。

DynamicData的set()和get()函数上的Id值对应于数组索引。为了简化数组元素的管理,如果给定的索引为空,DynamicData类中的每个set()函数都会创建该项。

为了简化这种类型的内存管理,DynamicData具有以下功能:

  • insert_array_data():在数组末尾创建一个新元素,并返回新元素的id。

  • remove_array_data():清除给定索引的元素。

  • clear_data():删除数组的所有元素。

  • get_array_index():返回位置id,给出数组支持的每个维度上的索引向量,这在多维数组中很有用。

// Create an array DynamicTypeBuilder for a 2x2 elements of type int32
std::vector<uint32_t> lengths = { 2, 2 };
DynamicType_ptr base_type = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicTypeBuilder_ptr builder =
        DynamicTypeBuilderFactory::get_instance()->create_array_builder(base_type, lengths);
 
// Create the data instance
DynamicType_ptr array_type = builder->build();
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(array_type));
 
// Access elements in the multidimensional array
MemberId pos = data->get_array_index({1, 0});
data->set_int32_value(11, pos);
data->set_int32_value(27, pos + 1);
data->clear_array_data(pos);

11、Map(实际中很少使用)

映射包含“键值”对类型的列表,允许用户插入、删除或修改映射的元素类型。与序列的主要区别在于,映射使用成对的元素,并创建关键元素的副本,以阻止对这些元素的访问。

要创建映射,用户必须设置键和值元素的类型,以及映射的大小限制(可选)。

为了简化这种类型的内存管理,DynamicData具有以下功能:

  • insert_map_data():插入新的键值对,并返回新创建的键值元素的id。

  • remove_map_data():使用给定的id查找key元素,并从映射中删除key和value元素。

  • clear_data():从地图中删除所有元素。

// Create DynamicTypeBuilder for a map of two pairs of {key:int32, value:int32}
uint32_t length = 2;
DynamicType_ptr base = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicTypeBuilder_ptr builder =
        DynamicTypeBuilderFactory::get_instance()->create_map_builder(base, base, length);
 
// Create the data instance
DynamicType_ptr map_type = builder->build();
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(map_type));
 
// Add a new element to the map with key 1
DynamicData_ptr key(DynamicDataFactory::get_instance()->create_data(base));
MemberId keyId;
MemberId valueId;
key->set_int32_value(1);
data->insert_map_data(key.get(), keyId, valueId);
 
// Add a new element to the map with key 2
// insert_map_data creates a copy of the key, so the same instance can be reused
MemberId keyId2;
MemberId valueId2;
key->set_int32_value(2);
data->insert_map_data(key.get(), keyId2, valueId2);
 
// Set the value to the element with key 2, using the returned value Id
data->set_int32_value(53, valueId2);
 
// Remove elements from the map
data->remove_map_data(keyId);
data->remove_map_data(keyId2);

二、复杂动态类型(由基本动态类型构建)

如果应用程序的数据模型是复杂的,则可以组合基本类型来创建复杂类型,包括嵌套组合类型(联合内结构内的结构)。还可以使用继承扩展类型,从而提高数据类型定义的灵活性,以适应模型。

以下小节描述了这些复杂类型及其用途。

  • Nested structures

  • Structure inheritance

  • Aliasof an alias(别名的别名,自己给自己找麻烦,不再介绍使用)

  • Unionswith complex types

1、Nested structures(嵌套结构体)

结构可以包含其他结构作为成员。对这些复合成员的访问由DynamicData实例限制和管理。用户必须在使用loan_value之前请求访问,并在完成后使用return_loned_value释放它们。如果该成员已被借出且尚未被释放,则借出操作将失败。

// Create astruct type
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
builder->add_member(0, "first", DynamicTypeBuilderFactory::get_instance()->create_int32_type());
builder->add_member(1, "other", DynamicTypeBuilderFactory::get_instance()->create_uint64_type());
DynamicType_ptr struct_type = builder->build();
 
// Create astruct type with the previous struct as member
DynamicTypeBuilder_ptr parent_builder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
parent_builder->add_member(0, "child_struct", struct_type);
parent_builder->add_member(1, "second", DynamicTypeBuilderFactory::get_instance()->create_int32_type());
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(parent_builder.get()));
 
// Access thechild struct with the loan operations
DynamicData* child_data = data->loan_value(0);
child_data->set_int32_value(5, 0);
child_data->set_uint64_value(13, 1);
data->return_loaned_value(child_data);

2、Structure inheritance(结构体继承)

要从另一个结构继承结构,请使用DynamicTypeBuilderFactory中的create_child_struct_type函数。结果类型包含基类中的所有成员以及添加到子类中的新成员。

结构支持多个继承级别,因此基类本身可以是另一个派生类型。

// Create a basestruct type
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
builder->add_member(0, "first", DynamicTypeBuilderFactory::get_instance()->create_int32_type());
builder->add_member(1, "other", DynamicTypeBuilderFactory::get_instance()->create_uint64_type());
 
// Create astruct type derived from the previous struct
DynamicTypeBuilder_ptr child_builder =
        DynamicTypeBuilderFactory::get_instance()->create_child_struct_builder(builder.get());
 
// Add newmembers to the derived type
builder->add_member(2, "third", DynamicTypeBuilderFactory::get_instance()->create_uint64_type());
 
// Create thedata instance
DynamicType_ptr struct_type = child_builder->build();
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(struct_type));
 
// The derivedtype includes the members defined on the base type
data->set_int32_value(5, 0);
data->set_uint64_value(13, 1);
data->set_uint64_value(47, 2);

3、Unions with complextypes(复杂类型构成的联合体)

联合体支持复杂类型字段。对这些复杂类型字段的访问由DynamicData实例限制和管理。用户必须在使用loan_value之前请求访问,并在完成后使用return_loned_value释放它们。如果字段已被借出且尚未释放,则借出操作将失败。

// Create aunion DynamicTypeBuilder
DynamicType_ptr discriminator = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicTypeBuilder_ptr builder = DynamicTypeBuilderFactory::get_instance()->create_union_builder(discriminator);
 
// Add a int32to the union
builder->add_member(0, "first", DynamicTypeBuilderFactory::get_instance()->create_int32_type(), "", { 0 },
        true);
 
// Create astruct type and add it to the union
DynamicTypeBuilder_ptr struct_builder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
struct_builder->add_member(0, "first", DynamicTypeBuilderFactory::get_instance()->create_int32_type());
struct_builder->add_member(1, "other", DynamicTypeBuilderFactory::get_instance()->create_uint64_type());
builder->add_member(1, "first", struct_builder.get(), "", { 1 }, false);
 
// Create theunion data instance
DynamicType_ptr union_type = builder->build();
DynamicData_ptr data(DynamicDataFactory::get_instance()->create_data(union_type));
 
// Access thestruct member using the loan operations
DynamicData* child_data = data->loan_value(1);
child_data->set_int32_value(9, 0);
child_data->set_int64_value(13, 1);
data->return_loaned_value(child_data);

三、动态类型内存管理

内存管理对于动态类型至关重要,因为每个动态类型和动态数据都是用指针管理的。存储在动态对象中的每个对象都由其所有者管理,用户必须删除他们使用工厂创建的每个对象。

DynamicTypeBuilder* pBuilder = DynamicTypeBuilderFactory::get_instance()->create_uint32_builder();
DynamicType_ptr pType = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicData* pData = DynamicDataFactory::get_instance()->create_data(pType);
 
DynamicTypeBuilderFactory::get_instance()->delete_builder(pBuilder);
DynamicDataFactory::get_instance()->delete_data(pData);

为了简化此管理,库定义了智能指针(DynamicTypeBuilder_ptr、DynamicType和DynamicData_ptr),这些指针将在不再需要对象时自动删除对象。DynamicType将始终返回为DynamicType_ptr,因为它的内存没有内部管理。

DynamicTypeBuilder_ptr pBuilder = DynamicTypeBuilderFactory::get_instance()->create_uint32_builder();
DynamicType_ptr pType = DynamicTypeBuilderFactory::get_instance()->create_int32_type();
DynamicData_ptr pData(DynamicDataFactory::get_instance()->create_data(pType));

唯一不能使用这些智能指针的情况是函数loan_value和return_loned_value。原始指针应与这些函数一起使用,因为不应删除返回的值,并且与它们一起使用智能指针会导致崩溃。

猜你喜欢

转载自blog.csdn.net/weixin_43369786/article/details/129474863