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。原始指针应与这些函数一起使用,因为不应删除返回的值,并且与它们一起使用智能指针会导致崩溃。