14. XTypes(可扩展类型)
eProsima Fast DDS 支持 OMG(对象管理组织)制定的《DDS 可扩展动态主题类型规范》(通常简称为 XTypes)。该规范定义了以下核心概念:
- DDS 支持的类型系统,包括可随时间演进的可扩展类型概念。
- 类型表示方式,涵盖 IDL(接口定义语言)和 TypeObject(类型对象)两种表示形式。
- 数据的有线传输表示(即数据在网络中传输时的编码格式)。
- 语言绑定,同时定义了静态语言绑定和动态语言绑定:
- eProsima Fast DDS-Gen(Fast DDS 代码生成工具)可根据 IDL 类型表示生成静态语言绑定代码。
- “动态语言绑定” 章节将详细说明用于定义、设置和读取数据类型的必要 API(应用程序编程接口)。
- DDS 内置的远程数据类型自动发现机制,更多细节可参考 “远程数据类型发现” 章节。
14.1 远程数据类型发现
DDS-XTypes 规范定义了一种内部机制,可在运行时发现远程数据类型,并根据通过 TypeConsistencyEnforcementQosPolicy(类型一致性强制服务质量策略) 配置的可扩展类型兼容性规则进行匹配。
远程数据类型发现机制基于数据类型信息的交换实现,且对这些信息进行了优化以减少所需带宽。一方面,下文 IDL(接口定义语言)中定义的 TypeInformation(类型信息) 结构体(摘自 DDS-XTypes 规范的附录 B),用于传递主题数据类型及其依赖项。
@extensibility(APPENDABLE) @nested
struct TypeIdentifierWithSize
{
TypeIdentifier type_id; // 类型标识符
unsigned long typeobject_serialized_size; // TypeObject 序列化后的大小
};
@extensibility(APPENDABLE) @nested
struct TypeIdentifierWithDependencies
{
TypeIdentifierWithSize typeid_with_size; // 带大小的类型标识符
long dependent_type_id_count; // 依赖类型标识符数量
sequence<TypeIdentifierWithSize> dependent_typeids; // 依赖类型标识符序列
};
@extensibility(MUTABLE) @nested
struct TypeInformation
{
@id(0x1001) TypeIdentifierWithDependencies minimal; // 最小化类型标识符及依赖项(ID:0x1001)
@id(0x1002) TypeIdentifierWithDependencies complete; // 完整类型标识符及依赖项(ID:0x1002)
};
TypeInformation 中包含 TypeIdentifier(类型标识符),这是对数据类型信息进行哈希处理后得到的值,可近乎唯一地标识该数据类型。而数据类型的具体信息则包含在 TypeObject(类型对象) 联合中:
@extensibility(APPENDABLE) @nested
union TypeObject switch(octet) // 以 octet(字节)类型为分支判断条件的联合
{
case EK_COMPLETE: // 分支:完整类型
CompleteTypeObject complete; // 完整类型对象
case EK_MINIMAL: // 分支:最小化类型
MinimalTypeObject minimal; // 最小化类型对象
};
其中,CompleteTypeObject(完整类型对象) 包含数据类型的完整描述信息;另一方面,MinimalTypeObject(最小化类型对象) 仅包含验证类型兼容性所需的最基础信息。
重要提示:当前 TypeObject 表示的实现不支持前向声明,也不支持使用
@external注解定义的递归数据类型。因此,当使用 Fast DDS-Gen(Fast DDS 代码生成工具)生成代码时,若需避免此类问题,请记得使用-no-typeobjectsupport选项禁用 TypeObject 生成代码。
14.1.1 前提条件
远程数据类型发现功能仅在满足以下条件时方可正常工作:
- 本地数据类型必须注册到
ITypeObjectRegistry(TypeObject 注册表接口) 中。若使用 eProsima Fast DDS-Gen 生成了注册所需的代码,那么调用register_type()/register_type()(注:原文两处函数名重复,推测为接口重载或笔误,均指 “类型注册函数”)时,类型会自动完成注册。Fast DDS-Gen 默认会生成所需的文件(即<IDL文件名>TypeObjectSupport.cxx和<IDL文件名>TypeObjectSupport.hpp)。注意:
-no-typeobjectsupport选项会禁用上述文件的生成,进而实际禁用远程类型发现功能。 TypeInformation需随 DomainParticipant(域参与者) 的端点发现信息一同接收。若某个 DomainParticipant 未提供其TypeInformation,则不会触发远程数据类型发现机制。
若上述前提条件未满足,端点匹配将仅依赖 类型名 和 主题名 来匹配已发现的端点。
14.1.2 配置
本地数据类型的传播级别可按照《类型传播》(Type Propagation,注:指原文对应章节)中的说明进行配置。
14.1.3 远程类型发现示例
有关如何利用此功能的更多信息,请参考《远程类型发现与端点匹配》(Remote type discovery and endpoint matching,注:指原文对应章节)。
14.2 动态语言绑定
动态语言绑定(Dynamic Language Binding)API 支持在运行时定义数据类型,无需像静态语言绑定(Plain Language Binding)那样预先定义类型。该 API 不仅包含类型定义功能,还提供了使用已定义类型所需的获取器(getters)和设置器(setters)方法。此外,类型定义还可通过以下两种方式实现:
- 如 “动态类型配置文件” 章节所述,通过 XML 配置文件定义;
- 如 “动态类型 IDL 解析” 章节所述,在运行时解析 IDL 文件定义。
本章将首先介绍动态语言绑定 API,随后详细说明支持的类型以及定义和使用这些类型的具体示例。
14.2.1 动态语言绑定接口
本节简要介绍动态语言绑定 API。如需更多信息,请参考《DDS-XTypes 规范》及 API 参考文档。
14.2.1.1 类型描述符(TypeDescriptor)
TypeDescriptor 用于描述类型的状态。该接口的对象具有值语义,即 TypeDescriptor 的数据可进行深度复制和比较。
14.2.1.2 注解描述符(AnnotationDescriptor)
AnnotationDescriptor 用于描述应用于特定元素的用户自定义注解。该接口的对象具有值语义,即 AnnotationDescriptor 的数据可进行深度复制和比较。
14.2.1.3 成员描述符(MemberDescriptor)
MemberDescriptor 用于描述类型中特定成员的状态。该接口的对象具有值语义,即 MemberDescriptor 的数据可进行深度复制和比较。
14.2.1.4 原始文本描述符(VerbatimTextDescriptor)
VerbatimTextDescriptor 用于描述内置注解 @verbatim 的应用情况。该接口的对象具有值语义,即 VerbatimTextDescriptor 的数据可进行深度复制和比较。
14.2.1.5 动态类型构建器工厂(DynamicTypeBuilderFactory)
DynamicTypeBuilderFactory 是一个单例类( singleton ),其实例负责创建和销毁 DynamicTypeBuilder 对象。该类提供了通用的 DynamicTypeBuilderFactory::create_type API,同时也提供了用于定义字符串、序列等其他基础类型的专用 API。更多信息请参考 “支持的类型” 章节。
14.2.1.6 动态类型(DynamicType)
DynamicType 对象表示特定的类型定义。一旦 DynamicType 构建完成,其内容不可修改。该接口的对象具有引用语义,因此 API 会接收一个空引用(nil-reference),调用后该引用将指向正确的 DynamicType 地址。
14.2.1.7 动态类型成员(DynamicTypeMember)
DynamicTypeMember 表示 DynamicType 的数据成员。该接口的对象具有引用语义,因此 API 会接收一个空引用,调用后该引用将指向正确的 DynamicTypeMember 地址。
14.2.1.8 动态类型构建器(DynamicTypeBuilder)
DynamicTypeBuilder 接口支持实例化具体的 DynamicType 对象,同时也是创建 DynamicType 前对其进行配置的 “过渡状态”。在定义完成后,DynamicTypeBuilderFactory 会根据构建器中包含的信息创建 DynamicType。通过 DynamicTypeBuilder::build 方法可创建完全构造的 DynamicType。构建器在创建 DynamicType 后仍可重复使用,且对构建器的后续修改不会影响已创建的类型。
14.2.1.9 动态数据工厂(DynamicDataFactory)
DynamicDataFactory 是一个单例类,其实例负责从给定的 DynamicType 实例中创建和销毁 DynamicData 对象。
14.2.1.10 动态数据(DynamicData)
DynamicData 表示 DynamicType 的数据实例,提供访问和修改数据值的功能。每个 DynamicData 对象都对应一个由其 DynamicType 表示的类型对象。DynamicData 提供反射式的获取器和设置器,支持对单个数据样本进行操作。
14.2.2 支持的类型
本节将介绍支持的类型系统,包括使用动态语言绑定 API 和 XML 配置文件实例化这些特定类型的示例。C++ 示例中还包含实例化相应 DynamicData 样本以及设置和读取值的操作。
14.2.2.1 基本类型(Primitive Types)
基本类型具有自描述性,无需配置参数即可创建。DynamicTypeBuilderFactory 接口提供 DynamicTypeBuilderFactory::get_primitive_type 方法,用户可通过该方法直接获取对应的基本类型 DynamicType。DynamicData 类为每种基本数据类型都提供了专用的获取器和设置器。
下表列出了支持的基本类型及其对应的 TypeKind(类型种类)。TypeKind 用于从 DynamicTypeBuilderFactory 中查询特定的基本类型 DynamicType。
| C++ 类型 | TypeKind(类型种类) |
|---|---|
bool | TK_BOOLEAN(布尔型) |
char | TK_CHAR8(8 位字符型) |
wchar_t | TK_CHAR16(16 位宽字符型) |
uint8_t | TK_BYTE(字节型)/ TK_UINT8(8 位无符号整型) |
int8_t | TK_INT8(8 位有符号整型) |
int16_t | TK_INT16(16 位有符号整型) |
uint16_t | TK_UINT16(16 位无符号整型) |
int32_t | TK_INT32(32 位有符号整型) |
uint32_t | TK_UINT32(32 位无符号整型) |
int64_t | TK_INT64(64 位有符号整型) |
uint64_t | TK_UINT64(64 位无符号整型) |
float | TK_FLOAT32(32 位浮点型) |
double | TK_FLOAT64(64 位浮点型) |
long double | TK_FLOAT128(128 位浮点型) |
以下示例展示如何创建包含基本类型成员的结构体:
OMG-IDL 代码
struct PrimitivesStruct
{
boolean my_bool; // 布尔型
octet my_octet; // 字节型
char my_char; // 8 位字符型
wchar my_wchar; // 16 位宽字符型
long my_long; // 32 位有符号整型
unsigned long my_ulong; // 32 位无符号整型
int8 my_int8; // 8 位有符号整型
uint8 my_uint8; // 8 位无符号整型
short my_short; // 16 位有符号整型
unsigned short my_ushort; // 16 位无符号整型
long long my_longlong; // 64 位有符号整型
unsigned long long my_ulonglong; // 64 位无符号整型
float my_float; // 32 位浮点型
double my_double; // 64 位浮点型
long double my_longdouble; // 128 位浮点型
};
XML 代码
<struct name="PrimitivesStruct">
<member name="my_bool" type="boolean"/> <!-- 布尔型 -->
<member name="my_octet" type="byte"/> <!-- 字节型 -->
<member name="my_char" type="char8"/> <!-- 8 位字符型 -->
<member name="my_wchar" type="char16"/> <!-- 16 位宽字符型 -->
<member name="my_long" type="int32"/> <!-- 32 位有符号整型 -->
<member name="my_ulong" type="uint32"/> <!-- 32 位无符号整型 -->
<member name="my_int8" type="int8"/> <!-- 8 位有符号整型 -->
<member name="my_uint8" type="uint8"/> <!-- 8 位无符号整型 -->
<member name="my_short" type="int16"/> <!-- 16 位有符号整型 -->
<member name="my_ushort" type="uint16"/> <!-- 16 位无符号整型 -->
<member name="my_longlong" type="int64"/> <!-- 64 位有符号整型 -->
<member name="my_ulonglong" type="uint64"/> <!-- 64 位无符号整型 -->
<member name="my_float" type="float32"/> <!-- 32 位浮点型 -->
<member name="my_double" type="float64"/> <!-- 64 位浮点型 -->
<member name="my_longdouble" type="float128"/> <!-- 128 位浮点型 -->
</struct>
C++ 代码
// 定义包含多种基本类型成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("PrimitivesStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 定义并向结构体中添加基本类型成员
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
// 添加布尔型成员 my_bool
member_descriptor->name("my_bool");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_BOOLEAN));
struct_builder->add_member(member_descriptor);
// 添加字节型成员 my_octet
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_octet");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_BYTE));
struct_builder->add_member(member_descriptor);
// 添加 8 位字符型成员 my_char
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_char");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_CHAR8));
struct_builder->add_member(member_descriptor);
// 添加 16 位宽字符型成员 my_wchar
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_wchar");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_CHAR16));
struct_builder->add_member(member_descriptor);
// 添加 32 位有符号整型成员 my_long
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_long");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32));
struct_builder->add_member(member_descriptor);
// 添加 32 位无符号整型成员 my_ulong
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_ulong");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_UINT32));
struct_builder->add_member(member_descriptor);
// 添加 8 位有符号整型成员 my_int8
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_int8");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT8));
struct_builder->add_member(member_descriptor);
// 添加 8 位无符号整型成员 my_uint8
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_uint8");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_UINT8));
struct_builder->add_member(member_descriptor);
// 添加 16 位有符号整型成员 my_short
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_short");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16));
struct_builder->add_member(member_descriptor);
// 添加 16 位无符号整型成员 my_ushort
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_ushort");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_UINT16));
struct_builder->add_member(member_descriptor);
// 添加 64 位有符号整型成员 my_longlong
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_longlong");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT64));
struct_builder->add_member(member_descriptor);
// 添加 64 位无符号整型成员 my_ulonglong
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_ulonglong");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_UINT64));
struct_builder->add_member(member_descriptor);
// 添加 32 位浮点型成员 my_float
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_float");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_FLOAT32));
struct_builder->add_member(member_descriptor);
// 添加 64 位浮点型成员 my_double
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_double");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_FLOAT64));
struct_builder->add_member(member_descriptor);
// 添加 128 位浮点型成员 my_longdouble
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_longdouble");
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_FLOAT128));
struct_builder->add_member(member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 为 int32_t 类型的成员设置并获取值
int32_t in_value {2}; // 待设置的值
int32_t out_value {0}; // 用于存储获取到的值
data->set_int32_value(data->get_member_id_by_name("my_long"), in_value); // 设置值
data->get_int32_value(out_value, data->get_member_id_by_name("my_long")); // 获取值
有关该类型的 XML 定义详细说明,请参考 “XML 基本类型” 章节。
14.2.2.1.1 类型提升(Type Promotions)
动态语言绑定还支持类型提升,允许在 get() 和 set() 操作中隐式提升类型。这意味着小范围类型可隐式提升为大范围类型,但反之则不允许。
下表列出了支持的类型提升规则:
| 原 TypeKind(类型种类) | 允许提升的目标 TypeKind(类型种类) |
|---|---|
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 | 无(无法进一步提升) |
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 | 任意类型(可提升为所有支持的类型) |
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)
字符串类型是字符(TK_CHAR8 或 TK_CHAR16)的一维集合。其中,TK_CHAR16 类型的字符串也称为 “宽字符串”(wide strings)或 wstring。用于标识字符串类型的 TypeKind 为 TK_STRING8(8 位字符串)和 TK_STRING16(16 位宽字符串)。
字符串可分为有界字符串(设置最大长度)和无界字符串(不限制长度),通过 TypeDescriptor 的 bound(边界)属性配置。
DynamicTypeBuilderFactory 提供 DynamicTypeBuilderFactory::create_string_type(创建 8 位字符串类型)和 DynamicTypeBuilderFactory::create_wstring_type(创建 16 位宽字符串类型)方法,简化字符串类型的创建过程 —— 只需传入最大长度参数即可(无界字符串使用 LENGTH_UNLIMITED)。
DynamicData 类也为字符串类型提供了专用的获取器和设置器:
- 8 位字符串:
DynamicData::get_string_value、DynamicData::set_string_value - 16 位宽字符串:
DynamicData::get_wstring_value、DynamicData::set_wstring_value
以下示例展示字符串类型的定义与使用:
OMG-IDL 代码
struct StringsStruct
{
string my_string; // 无界 8 位字符串
wstring my_wstring; // 无界 16 位宽字符串
string<41925> my_bounded_string; // 最大长度为 41925 的有界 8 位字符串
wstring<20925> my_bounded_wstring; // 最大长度为 20925 的有界 16 位宽字符串
};
XML 代码
<struct name="StringsStruct">
<member name="my_string" type="string"/> <!-- 无界 8 位字符串 -->
<member name="my_wstring" type="wstring"/> <!-- 无界 16 位宽字符串 -->
<member name="my_bounded_string" type="string" stringMaxLength="41925"/> <!-- 有界 8 位字符串,最大长度 41925 -->
<member name="my_bounded_wstring" type="wstring" stringMaxLength="20925"/> <!-- 有界 16 位宽字符串,最大长度 20925 -->
</struct>
C++ 代码
// 定义包含字符串成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("StringsStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 定义并向结构体中添加字符串成员
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
// 添加无界 8 位字符串成员 my_string
member_descriptor->name("my_string");
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_string_type(static_cast<uint32_t>(LENGTH_UNLIMITED))->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_STRING8); // 设置为 8 位字符串类型
type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_CHAR8)); // 字符类型为 TK_CHAR8
type_descriptor->bound().push_back(static_cast<uint32_t>(LENGTH_UNLIMITED)); // 无界
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(member_descriptor);
// 添加无界 16 位宽字符串成员 my_wstring
member_descriptor->name("my_wstring");
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_wstring_type(static_cast<uint32_t>(LENGTH_UNLIMITED))->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_STRING16); // 设置为 16 位宽字符串类型
type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_CHAR16)); // 字符类型为 TK_CHAR16
type_descriptor->bound().push_back(static_cast<uint32_t>(LENGTH_UNLIMITED)); // 无界
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(member_descriptor);
// 添加有界 8 位字符串成员 my_bounded_string(最大长度 41925)
member_descriptor->name("my_bounded_string");
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_string_type(41925)->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_STRING8);
type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_CHAR8));
type_descriptor->bound().push_back(41925); // 最大长度 41925
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(member_descriptor);
// 添加有界 16 位宽字符串成员 my_bounded_wstring(最大长度 20925)
member_descriptor->name("my_bounded_wstring");
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_wstring_type(20925)->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_STRING16);
type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_CHAR16));
type_descriptor->bound().push_back(20925); // 最大长度 20925
member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 为字符串成员设置并获取值
std::string in_value {"helloworld"}; // 待设置的字符串值
std::string out_value; // 用于存储获取到的字符串值
data->set_string_value(data->get_member_id_by_name("my_string"), in_value); // 设置值
data->get_string_value(out_value, data->get_member_id_by_name("my_string")); // 获取值
有关该类型的 XML 定义详细说明,请参考 “XML 字符串类型” 章节。
14.2.2.3 枚举类型(Enumeration Types)
枚举类型包含一组支持的值(枚举常量,enumeration literals),且在这些值中选择一个作为当前值。用于标识枚举类型的 TypeKind 为 TK_ENUM。
需通过 DynamicTypeBuilder 的 DynamicTypeBuilder::add_member 方法配置枚举常量 —— 为每个支持的值调用该方法,并传入对应的 MemberDescriptor。传入的 MemberDescriptor 需通过 name(名称)属性指定枚举常量的名称。
枚举的底层基本类型通过 MemberDescriptor 的 type(类型)属性配置。由于底层类型在添加第一个枚举常量时确定,后续所有枚举常量必须使用相同的基本类型,以保证枚举类型的一致性。此外,也可通过设置 TypeDescriptor 的 literal_type(常量类型)属性强制指定底层类型 —— 若已设置 literal_type,但第一个枚举常量的类型与之一致,则调用 DynamicTypeBuilder::add_member 时会失败。
另外,可通过 MemberDescriptor 的 literal_value(常量值)属性设置枚举常量的值,其效果等同于使用内置注解 @value。若要修改枚举类型的默认常量,可设置 MemberDescriptor 的 is_default_literal(是否为默认常量)属性,其效果等同于使用内置注解 @default_literal。
枚举类型本质上是一种 “仅能取特定值的有符号整型”,因此 DynamicData 中对应枚举类型的获取器和设置器,与枚举底层有符号整型的获取器和设置器一致(也支持可提升至该底层类型的其他方法)。
以下示例展示枚举类型的定义与使用:
OMG-IDL 代码
// 定义枚举类型 MyEnum
enum MyEnum
{
A, // 枚举常量 A
B, // 枚举常量 B
C // 枚举常量 C
};
// 定义包含枚举成员的结构体
struct EnumStruct
{
MyEnum my_enum; // 枚举类型成员
};
XML 代码
<!-- 定义枚举类型 MyEnum -->
<enum name="MyEnum">
<enumerator name="A" value="0"/> <!-- 枚举常量 A,值为 0 -->
<enumerator name="B" value="1"/> <!-- 枚举常量 B,值为 1 -->
<enumerator name="C"/> <!-- 枚举常量 C,值默认递增为 2 -->
</enum>
<!-- 定义包含枚举成员的结构体 -->
<struct name="EnumStruct">
<member name="my_enum" type="nonBasic" nonBasicTypeName="MyEnum"/> <!-- 非基本类型成员,关联 MyEnum -->
</struct>
C++ 代码
// 定义 C++ 枚举类型(底层为 int32_t)
enum MyEnum : int32_t
{
A, // 值为 0
B, // 值为 1
C // 值为 2
};
// 定义包含枚举成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("EnumStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 定义枚举类型 MyEnum
TypeDescriptor::_ref_type enum_type_descriptor {traits<TypeDescriptor>::make_shared()};
enum_type_descriptor->kind(TK_ENUM); // 设置类型为枚举
enum_type_descriptor->name("MyEnum"); // 设置枚举名称
DynamicTypeBuilder::_ref_type enum_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(enum_type_descriptor)
};
// 向枚举类型中添加枚举常量
MemberDescriptor::_ref_type enum_member_descriptor {traits<MemberDescriptor>::make_shared()};
// 添加枚举常量 A(底层类型为 int32_t)
enum_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32));
enum_member_descriptor->name("A");
enum_builder->add_member(enum_member_descriptor);
// 添加枚举常量 B
enum_member_descriptor = traits<MemberDescriptor>::make_shared();
enum_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32));
enum_member_descriptor->name("B");
enum_builder->add_member(enum_member_descriptor);
// 添加枚举常量 C
enum_member_descriptor = traits<MemberDescriptor>::make_shared();
enum_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32));
enum_member_descriptor->name("C");
enum_builder->add_member(enum_member_descriptor);
// 构建枚举类型
DynamicType::_ref_type enum_type = enum_builder->build();
// 向结构体中添加枚举成员 my_enum
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
member_descriptor->name("my_enum");
member_descriptor->type(enum_type); // 关联枚举类型 MyEnum
struct_builder->add_member(member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 为枚举成员设置并获取值
MyEnum in_value {MyEnum::C}; // 方式 1:直接使用 C++ 枚举值(选择 MyEnum::C)
/* 方式 2:使用无符号整型值(选择 MyEnum::C,值为 2)
uint32_t in_value {2};
*/
uint32_t out_value {0}; // 用于存储获取到的值
data->set_uint32_value(data->get_member_id_by_name("my_enum"), in_value); // 设置值
data->get_uint32_value(out_value, data->get_member_id_by_name("my_enum")); // 获取值
有关该类型的 XML 定义详细说明,请参考 “XML 枚举类型” 章节。
14.2.2.4 位掩码类型(Bitmask Types)
位掩码类型是布尔标志(bitflags,位标志)的集合,支持单独设置每个标志。用于标识位掩码类型的 TypeKind 为 TK_BITMASK。
位掩码的边界(即最大位数)需通过 TypeDescriptor 的 bound(边界)属性设置,允许的最大边界为 64 位。
需通过 DynamicTypeBuilder 的 DynamicTypeBuilder::add_member 方法配置位标志 —— 每个位标志通过 MemberDescriptor 描述,其中 name(名称)属性指定位标志的名称。位标志的底层基本类型必须为 TK_BOOLEAN(布尔型),并通过 MemberDescriptor 的 type(类型)属性设置。此外,可通过 MemberDescriptor 的 position(位置)属性指定位标志在掩码中的位位置,其效果等同于使用内置注解 @position;若未指定位置,则按添加顺序依次分配位置。
DynamicTypeBuilderFactory 提供 DynamicTypeBuilderFactory::create_bitmask_type 方法,简化位掩码类型的创建过程。
操作位掩码类型有两种方式:
- 使用
DynamicData::get_boolean_value/DynamicData::set_boolean_value:设置或获取特定的位标志; - 使用与位掩码边界对应的无符号整型获取器 / 设置器:一次性设置多个位标志(未命名的位始终为未设置状态)。
以下示例展示位掩码类型的定义与使用:
OMG-IDL 代码
// 定义位掩码类型 MyBitMask(位边界为 8 位)
@bit_bound(8)
bitmask MyBitMask
{
@position(0) flag0, // 位 0:位标志 flag0
flag1, // 位 1:位标志 flag1(位置默认递增)
flag2, // 位 2:位标志 flag2
@position(5) flag5 // 位 5:位标志 flag5
};
// 定义包含位掩码成员的结构体
struct BitmaskStruct
{
MyBitMask my_bitmask; // 位掩码类型成员
};
XML 代码
<!-- 定义位掩码类型 MyBitMask(位边界为 8 位) -->
<bitmask name="MyBitMask" bit_bound="8">
<bit_value name="flag0" position="0"/> <!-- 位 0:flag0 -->
<bit_value name="flag1"/> <!-- 位 1:flag1 -->
<bit_value name="flag2"/> <!-- 位 2:flag2 -->
<bit_value name="flag5" position="5"/> <!-- 位 5:flag5 -->
</bitmask>
<!-- 定义包含位掩码成员的结构体 -->
<struct name="BitmaskStruct">
<member name="my_bitmask" type="nonBasic" nonBasicTypeName="MyBitMask"/> <!-- 非基本类型成员,关联 MyBitMask -->
</struct>
C++ 代码
// 定义包含位掩码成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("BitmaskStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 定义位掩码类型 MyBitMask(位边界为 8 位)
DynamicTypeBuilder::_ref_type bitmask_builder {
DynamicTypeBuilderFactory::get_instance()->create_bitmask_type(8)
};
/* 替代方式:通过 TypeDescriptor 手动配置
TypeDescriptor::_ref_type bitmask_type_descriptor {traits<TypeDescriptor>::make_shared()};
bitmask_type_descriptor->kind(TK_BITMASK); // 设置类型为位掩码
bitmask_type_descriptor->name("MyBitMask"); // 设置位掩码名称
bitmask_type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_BOOLEAN)); // 元素类型为布尔型
bitmask_type_descriptor->bound().push_back(8); // 位边界为 8 位
DynamicTypeBuilder::_ref_type bitmask_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(bitmask_type_descriptor)
};
*/
// 向位掩码类型中添加位标志
MemberDescriptor::_ref_type bitfield_member_descriptor {traits<MemberDescriptor>::make_shared()};
// 添加位标志 flag0(位位置 0)
bitfield_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_BOOLEAN));
bitfield_member_descriptor->name("flag0");
bitfield_member_descriptor->position(0); // 指定位位置为 0
bitmask_builder->add_member(bitfield_member_descriptor);
// 添加位标志 flag1(位位置 1,默认递增)
bitfield_member_descriptor = traits<MemberDescriptor>::make_shared();
bitfield_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_BOOLEAN));
bitfield_member_descriptor->name("flag1");
bitmask_builder->add_member(bitfield_member_descriptor);
// 添加位标志 flag2(位位置 2,默认递增)
bitfield_member_descriptor = traits<MemberDescriptor>::make_shared();
bitfield_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_BOOLEAN));
bitfield_member_descriptor->name("flag2");
bitmask_builder->add_member(bitfield_member_descriptor);
// 添加位标志 flag5(位位置 5)
bitfield_member_descriptor = traits<MemberDescriptor>::make_shared();
bitfield_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_BOOLEAN));
bitfield_member_descriptor->name("flag5");
bitfield_member_descriptor->position(5); // 指定位位置为 5
bitmask_builder->add_member(bitfield_member_descriptor);
// 构建位掩码类型
DynamicType::_ref_type bitmask_type = bitmask_builder->build();
// 向结构体中添加位掩码成员 my_bitmask
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
member_descriptor->name("my_bitmask");
member_descriptor->type(bitmask_type); // 关联位掩码类型 MyBitMask
struct_builder->add_member(member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 方式 1:通过无符号整型一次性设置/获取多位(设置 flag0 和 flag1)
uint8_t in_value {3}; // 二进制 00000011,对应 flag0(位 0)和 flag1(位 1)为 1
uint8_t out_value {0}; // 用于存储获取到的值
data->set_uint8_value(data->get_member_id_by_name("my_bitmask"), in_value); // 设置值
data->get_uint8_value(out_value, data->get_member_id_by_name("my_bitmask")); // 获取值
// 方式 2:单独设置/获取特定位标志(设置 flag5)
bool in_bitflag_value = true; // 待设置的位标志值(true 表示设置)
bool out_bitflag_value = false; // 用于存储获取到的位标志值
DynamicData::_ref_type bitmask_data = data->loan_value(data->get_member_id_by_name("my_bitmask")); // 借出位掩码数据的引用
bitmask_data->set_boolean_value(bitmask_data->get_member_id_by_name("flag5"), in_bitflag_value); // 设置 flag5
bitmask_data->get_boolean_value(out_bitflag_value, bitmask_data->get_member_id_by_name("flag5")); // 获取 flag5
有关该类型的 XML 定义详细说明,请参考 “XML 位掩码类型” 章节。
14.2.2.5 别名类型(Alias Types)
别名类型为已存在的类型提供一个 “替代名称”(即别名)。用于标识别名类型的 TypeKind 为 TK_ALIAS。
定义别名类型时,除了指定别名名称,还需通过 TypeDescriptor 的 base_type(基础类型)属性设置其对应的基础类型。别名类型支持递归定义 —— 即可以将另一个别名类型作为基础类型。
一旦创建 DynamicData 对象,对别名类型数据的访问方式与访问其基础类型数据的方式完全一致。
以下示例展示别名类型的定义与使用:
OMG-IDL 代码
// 为枚举类型 MyEnum 定义别名 MyAliasedEnum
typedef MyEnum MyAliasedEnum;
// 为有界字符串类型 string<100> 定义别名 MyAliasedBoundedString
typedef string<100> MyAliasedBoundedString;
// 为别名类型 MyAliasedEnum 定义递归别名 MyRecursiveAlias
typedef MyAliasedEnum MyRecursiveAlias;
// 定义包含别名类型成员的结构体
struct AliasStruct
{
MyAliasedEnum my_aliased_enum; // 别名类型成员(基础类型为 MyEnum)
MyAliasedBoundedString my_aliased_bounded_string; // 别名类型成员(基础类型为 string<100>)
MyRecursiveAlias my_recursive_alias; // 递归别名类型成员(基础类型为 MyAliasedEnum)
};
XML 代码
<!-- 为枚举类型 MyEnum 定义别名 MyAliasedEnum -->
<typedef name="MyAliasedEnum" type="nonBasic" nonBasicTypeName="MyEnum"/>
<!-- 为字符串类型定义别名 MyAliasedBoundedString(注:XSD 不支持为别名字符串设置边界) -->
<typedef name="MyAliasedBoundedString" type="string"/>
<!-- 为别名类型 MyAliasedEnum 定义递归别名 MyRecursiveAlias -->
<typedef name="MyRecursiveAlias" type="nonBasic" nonBasicTypeName="MyAliasedEnum"/>
<!-- 定义包含别名类型成员的结构体 -->
<struct name="AliasStruct">
<member name="my_aliased_enum" type="nonBasic" nonBasicTypeName="MyAliasedEnum"/> <!-- 关联 MyAliasedEnum -->
<member name="my_aliased_bounded_string" type="nonBasic" nonBasicTypeName="MyAliasedBoundedString"/> <!-- 关联 MyAliasedBoundedString -->
<member name="my_recursive_alias" type="nonBasic" nonBasicTypeName="MyRecursiveAlias"/> <!-- 关联 MyRecursiveAlias -->
</struct>
C++ 代码
// 定义包含别名类型成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("AliasStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 1. 为枚举类型 MyEnum 定义别名 MyAliasedEnum
TypeDescriptor::_ref_type aliasenum_type_descriptor {traits<TypeDescriptor>::make_shared()};
aliasenum_type_descriptor->kind(TK_ALIAS); // 设置类型为别名
aliasenum_type_descriptor->name("MyAliasedEnum"); // 别名名称
aliasenum_type_descriptor->base_type(enum_type); // 基础类型为之前定义的 MyEnum
DynamicTypeBuilder::_ref_type aliasenum_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(aliasenum_type_descriptor)
};
// 构建别名类型 MyAliasedEnum
DynamicType::_ref_type aliasenum_type = aliasenum_builder->build();
// 2. 为有界字符串类型(最大长度 100)定义别名 MyAliasedBoundedString
TypeDescriptor::_ref_type alias_bounded_string_type_descriptor {traits<TypeDescriptor>::make_shared()};
alias_bounded_string_type_descriptor->kind(TK_ALIAS); // 设置类型为别名
alias_bounded_string_type_descriptor->name("MyAliasedBoundedString"); // 别名名称
// 基础类型为最大长度 100 的 8 位字符串
alias_bounded_string_type_descriptor->base_type(
DynamicTypeBuilderFactory::get_instance()->create_string_type(100)->build()
);
DynamicTypeBuilder::_ref_type alias_bounded_string_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(alias_bounded_string_type_descriptor)
};
// 构建别名类型 MyAliasedBoundedString
DynamicType::_ref_type alias_bounded_string_type = alias_bounded_string_builder->build();
// 3. 为别名类型 MyAliasedEnum 定义递归别名 MyRecursiveAlias
TypeDescriptor::_ref_type recursive_alias_type_descriptor {traits<TypeDescriptor>::make_shared()};
recursive_alias_type_descriptor->kind(TK_ALIAS); // 设置类型为别名
recursive_alias_type_descriptor->name("MyRecursiveAlias"); // 别名名称
recursive_alias_type_descriptor->base_type(aliasenum_type); // 基础类型为 MyAliasedEnum
DynamicTypeBuilder::_ref_type recursive_alias_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(recursive_alias_type_descriptor)
};
// 构建递归别名类型 MyRecursiveAlias
DynamicType::_ref_type recursive_alias_type = recursive_alias_builder->build();
// 向结构体中添加别名类型成员
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
// 添加 MyAliasedEnum 类型成员 my_aliased_enum
member_descriptor->name("my_aliased_enum");
member_descriptor->type(aliasenum_type);
struct_builder->add_member(member_descriptor);
// 添加 MyAliasedBoundedString 类型成员 my_aliased_bounded_string
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_aliased_bounded_string");
member_descriptor->type(alias_bounded_string_type);
struct_builder->add_member(member_descriptor);
// 添加 MyRecursiveAlias 类型成员 my_recursive_alias
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->name("my_recursive_alias");
member_descriptor->type(recursive_alias_type);
struct_builder->add_member(member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 为别名枚举成员设置并获取值(访问方式与基础类型一致)
MyEnum in_value {MyEnum::C}; // 待设置的值(MyEnum::C)
int32_t out_value {0}; // 用于存储获取到的值
data->set_int32_value(data->get_member_id_by_name("my_aliased_enum"), in_value); // 设置值
data->get_int32_value(out_value, data->get_member_id_by_name("my_aliased_enum")); // 获取值
有关该类型的 XML 定义详细说明,请参考 “XML 别名类型” 章节。
14.2.2.6 序列类型(Sequence Types)
序列类型是任意类型的一维集合。用于标识序列类型的 TypeKind 为 TK_SEQUENCE。
定义序列类型时,需通过 TypeDescriptor 的 element_type(元素类型)属性设置集合中元素的类型;同时,需通过 bound(边界)属性设置序列的最大长度(无界序列使用 LENGTH_UNLIMITED)。
DynamicTypeBuilderFactory 提供 DynamicTypeBuilderFactory::create_sequence_type 方法,简化序列类型的创建过程 —— 只需传入元素类型和序列边界(无界序列使用 LENGTH_UNLIMITED)即可。
DynamicData 类为每种基本类型的序列都提供了专用的 get_values() 和 set_values() 方法,方便用户操作基本类型序列(这些方法也支持类型提升)。对于复杂类型的序列,请参考 “复杂类型数据的管理” 章节。
若需修改序列中特定范围的值,可向 get_values() / set_values() 传入起始索引 —— 方法将仅处理从该索引开始、直至输入数据长度的元素。此外,也可通过 get_value() / set_value() 并传入元素索引,修改序列中特定位置的元素。
以下示例展示序列类型的定义与使用:
OMG-IDL 代码
// 定义包含序列成员的结构体
struct SequenceStruct
{
sequence<MyBitMask> bitmask_sequence; // 无界序列(元素类型为 MyBitMask)
sequence<short, 5> short_sequence; // 有界序列(元素类型为 short,最大长度 5)
};
XML 代码
<!-- 定义包含序列成员的结构体 -->
<struct name="SequenceStruct">
<!-- 无界序列:元素类型为 MyBitMask,sequenceMaxLength="-1" 表示无界 -->
<member name="bitmask_sequence" type="nonBasic" nonBasicTypeName="MyBitMask" sequenceMaxLength="-1"/>
<!-- 有界序列:元素类型为 int16(对应 short),最大长度 5 -->
<member name="short_sequence" sequenceMaxLength="5" type="int16"/>
</struct>
C++ 代码
// 定义包含序列成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("SequenceStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 1. 向结构体中添加无界序列成员 bitmask_sequence(元素类型为 MyBitMask)
MemberDescriptor::_ref_type sequence_member_descriptor {traits<MemberDescriptor>::make_shared()};
sequence_member_descriptor->name("bitmask_sequence");
// 序列类型:元素为 MyBitMask,无界(LENGTH_UNLIMITED)
sequence_member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_sequence_type(
bitmask_type, static_cast<uint32_t>(LENGTH_UNLIMITED)
)->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_SEQUENCE); // 设置类型为序列
type_descriptor->element_type(bitmask_type); // 元素类型为 MyBitMask
type_descriptor->bound().push_back(static_cast<uint32_t>(LENGTH_UNLIMITED)); // 无界
sequence_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(sequence_member_descriptor);
// 2. 向结构体中添加有界序列成员 short_sequence(元素类型为 int16,最大长度 5)
sequence_member_descriptor = traits<MemberDescriptor>::make_shared();
sequence_member_descriptor->name("short_sequence");
// 序列类型:元素为 int16(对应 short),最大长度 5
sequence_member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_sequence_type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16), 5
)->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_SEQUENCE); // 设置类型为序列
type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16)); // 元素类型为 int16
type_descriptor->bound().push_back(5); // 最大长度 5
sequence_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(sequence_member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 方式 1:设置/获取整个序列(short_sequence)
Int16Seq in_value = {1, 2}; // 待设置的序列值(包含两个元素)
Int16Seq out_value; // 用于存储获取到的序列值
data->set_int16_values(data->get_member_id_by_name("short_sequence"), in_value); // 设置序列
data->get_int16_values(out_value, data->get_member_id_by_name("short_sequence")); // 获取序列
// 方式 2:借出序列数据引用,操作特定范围或位置的元素
DynamicData::_ref_type sequence_data {data->loan_value(data->get_member_id_by_name("short_sequence"))};
// 设置序列中从索引 3 开始的两个元素(值为 1、2)
sequence_data->set_int16_values(3, in_value);
// 获取序列中从索引 1 开始的所有元素
sequence_data->get_int16_values(out_value, 1);
// 设置序列中索引 2 位置的元素(值为 8)
int16_t in_simple_value = 8;
int16_t out_simple_value;
sequence_data->set_int16_value(2, in_simple_value);
// 获取序列中索引 2 位置的元素
sequence_data->get_int16_value(out_simple_value, 2);
// 归还借出的序列数据引用
data->return_loaned_value(sequence_data);
有关该类型的 XML 定义详细说明,请参考 “XML 序列类型” 章节。
14.2.2.7 数组类型(Array Types)
数组类型是任意类型的多维集合。用于标识数组类型的 TypeKind 为 TK_ARRAY。
定义数组类型时,需通过 TypeDescriptor 的 element_type(元素类型)属性设置集合中元素的类型;同时,需通过 bound(边界)属性设置一个 “包含各维度大小的序列”—— 该序列至少包含一个维度,且所有维度的大小均不允许为 0。
DynamicTypeBuilderFactory 提供 DynamicTypeBuilderFactory::create_array_type 方法,简化数组类型的创建过程 —— 只需传入元素类型和 “各维度大小的序列” 即可。
DynamicData 类为每种基本类型的数组都提供了专用的 get_values() 和 set_values() 方法,方便用户操作基本类型数组(这些方法也支持类型提升)。对于复杂类型的数组,请参考 “复杂类型数据的管理” 章节。
注意:多维数组会被 “扁平化” 为一维数组处理。
若需修改数组中特定范围的值,可向 get_values() / set_values() 传入起始索引 —— 方法将仅处理从该索引开始、直至输入数据长度的元素。此外,也可通过 get_value() / set_value() 并传入元素索引,修改数组中特定位置的元素。
以下示例展示数组类型的定义与使用:
OMG-IDL 代码
// 定义包含数组成员的结构体
struct ArrayStruct
{
long long_array[2][3][4]; // 三维数组(元素类型为 long,维度为 2×3×4)
};
XML 代码
<!-- 定义包含数组成员的结构体 -->
<struct name="ArrayStruct">
<!-- 三维数组:元素类型为 int32(对应 long),维度为 2×3×4 -->
<member name="long_array" type="int32" arrayDimensions="2,3,4"/>
</struct>
C++ 代码
// 定义包含数组成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("ArrayStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 向结构体中添加三维数组成员 long_array(元素类型为 int32,维度 2×3×4)
MemberDescriptor::_ref_type array_member_descriptor {traits<MemberDescriptor>::make_shared()};
array_member_descriptor->name("long_array");
// 数组类型:元素为 int32,维度 2×3×4
array_member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_array_type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32), {2, 3, 4}
)->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_ARRAY); // 设置类型为数组
type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32)); // 元素类型为 int32
type_descriptor->bound().push_back(2); // 第一维度大小 2
type_descriptor->bound().push_back(3); // 第二维度大小 3
type_descriptor->bound().push_back(4); // 第三维度大小 4
array_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(array_member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 方式 1:设置/获取整个数组(扁平化处理后共 2×3×4=24 个元素)
Int32Seq in_value = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24};
Int32Seq out_value; // 用于存储获取到的数组值
data->set_int32_values(data->get_member_id_by_name("long_array"), in_value); // 设置数组
data->get_int32_values(out_value, data->get_member_id_by_name("long_array")); // 获取数组
// 方式 2:借出数组数据引用,操作特定范围或位置的元素
DynamicData::_ref_type array_data {data->loan_value(data->get_member_id_by_name("long_array"))};
// 设置数组中从索引 22 开始的两个元素(值为 0、1)
Int32Seq small_in_value = {0, 1};
array_data->set_int32_values(22, small_in_value);
// 获取数组中从索引 1 开始的所有元素
array_data->get_int32_values(out_value, 1);
// 设置数组中索引 2 位置的元素(值为 8)
int32_t in_simple_value = 8;
int32_t out_simple_value;
array_data->set_int32_value(2, in_simple_value);
// 获取数组中索引 2 位置的元素
array_data->get_int32_value(out_simple_value, 2);
// 归还借出的数组数据引用
data->return_loaned_value(array_data);
有关该类型的 XML 定义详细说明,请参考 “XML 数组类型” 章节。
14.2.2.8 映射类型(Map Types)
映射类型是键值对(key/value pair) 的集合,通过唯一的 “键”(key)访问对应的 “值”(value)。用于标识映射类型的 TypeKind 为 TK_MAP。
定义映射类型时,需通过 TypeDescriptor 的以下属性配置:
element_type(元素类型):设置映射中 “值” 的类型;key_type(键类型):设置映射中 “键” 的类型(支持的键类型包括有符号整型、无符号整型和字符串类型,暂不支持宽字符串作为键);bound(边界):设置映射的最大长度(无界映射使用LENGTH_UNLIMITED)。
DynamicTypeBuilderFactory 提供 DynamicTypeBuilderFactory::create_map_type 方法,简化映射类型的创建过程 —— 只需传入键类型、值类型和映射边界(无界映射使用 LENGTH_UNLIMITED)即可。
操作映射类型的步骤相对复杂,具体如下:
- 通过
DynamicData::get_member_id_by_nameAPI 获取特定 “键” 对应的MemberId(成员 ID):- 若该键已存在,直接返回对应的
MemberId; - 若该键不存在,自动创建该键并返回对应的
MemberId; - 调用该方法时,需传入键值的正确字符串表示形式。
- 若该键已存在,直接返回对应的
- 使用与 “值类型” 对应的 API 设置或获取映射中的值。
对于复杂类型的映射值,请参考 “复杂类型数据的管理” 章节。
以下示例展示映射类型的定义与使用:
OMG-IDL 代码
// 定义包含映射成员的结构体
struct MapStruct
{
// 无界映射:键类型为 string,值类型为 MyAliasedBoundedString
map<string, MyAliasedBoundedString> string_alias_unbounded_map;
// 有界映射:键类型为 short,值类型为 long,最大长度 2
map<short, long, 2> short_long_map;
};
XML 代码
<!-- 定义包含映射成员的结构体 -->
<struct name="MapStruct">
<!-- 无界映射:键类型为 string,值类型为 MyAliasedBoundedString,mapMaxLength="-1" 表示无界 -->
<member name="string_alias_unbounded_map" type="nonBasic" nonBasicTypeName="MyAliasedBoundedString" key_type="string" mapMaxLength="-1"/>
<!-- 有界映射:键类型为 int16(对应 short),值类型为 int32(对应 long),最大长度 2 -->
<member name="short_long_map" type="int32" key_type="int16" mapMaxLength="2"/>
</struct>
C++ 代码
// 定义包含映射成员的结构体类型
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("MapStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 1. 向结构体中添加无界映射成员 string_alias_unbounded_map(键:string,值:MyAliasedBoundedString)
MemberDescriptor::_ref_type map_member_descriptor {traits<MemberDescriptor>::make_shared()};
map_member_descriptor->name("string_alias_unbounded_map");
// 映射类型:键为无界 string,值为 MyAliasedBoundedString,映射无界
map_member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_map_type(
DynamicTypeBuilderFactory::get_instance()->create_string_type(static_cast<uint32_t>(LENGTH_UNLIMITED))->build(),
alias_bounded_string_type,
static_cast<uint32_t>(LENGTH_UNLIMITED)
)->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_MAP); // 设置类型为映射
type_descriptor->key_element_type(DynamicTypeBuilderFactory::get_instance()->create_string_type(static_cast<uint32_t>(LENGTH_UNLIMITED))->build()); // 键类型为无界 string
type_descriptor->element_type(alias_bounded_string_type); // 值类型为 MyAliasedBoundedString
type_descriptor->bound().push_back(static_cast<uint32_t>(LENGTH_UNLIMITED)); // 映射无界
map_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(map_member_descriptor);
// 2. 向结构体中添加有界映射成员 short_long_map(键:int16,值:int32,最大长度 2)
map_member_descriptor = traits<MemberDescriptor>::make_shared();
map_member_descriptor->name("short_long_map");
// 映射类型:键为 int16,值为 int32,映射最大长度 2
map_member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_map_type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16),
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32),
2
)->build()
);
/* 替代方式:通过 TypeDescriptor 手动配置
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_MAP); // 设置类型为映射
type_descriptor->key_element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16)); // 键类型为 int16
type_descriptor->element_type(DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32)); // 值类型为 int32
type_descriptor->bound().push_back(2); // 映射最大长度 2
map_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)->build());
*/
struct_builder->add_member(map_member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 借出映射数据引用(操作 short_long_map)
DynamicData::_ref_type map_data = data->loan_value(data->get_member_id_by_name("short_long_map"));
// 为映射设置并获取值(键为 1,值为 2)
int32_t key {1}; // 键值
int32_t in_value {2}; // 待设置的值
int32_t out_value; // 用于存储获取到的值
// 将键值转换为字符串,获取对应的 MemberId 并设置值
map_data->set_int32_value(map_data->get_member_id_by_name(std::to_string(key)), in_value);
// 获取值
map_data->get_int32_value(out_value, map_data->get_member_id_by_name(std::to_string(key)));
// 归还借出的映射数据引用
data->return_loaned_value(map_data);
有关该类型的 XML 定义详细说明,请参考 “XML 映射类型” 章节。
14.2.2.9 结构类型(Structure Types)
结构类型是不同类型成员的聚合。用于标识结构类型的 TypeKind 为 TK_STRUCTURE。
结构类型支持单继承—— 即一个结构类型可继承自另一个已定义的结构类型。被继承的结构类型需通过 TypeDescriptor 的 base_type(基础类型)属性配置。此外,可通过 TypeDescriptor 的 extensibility_kind(可扩展性种类)属性配置结构类型的可扩展性。
注意:当前暂不支持内置注解
@nested(嵌套)。
需通过 DynamicTypeBuilder 的 DynamicTypeBuilder::add_member 方法配置结构成员 —— 为每个成员调用该方法,并传入对应的 MemberDescriptor。
注意:允许定义无成员的 “空结构体”。
结构成员的配置规则如下:
- 通过
MemberDescriptor的name(名称)属性设置成员名称; - 通过
MemberDescriptor的type(类型)属性设置成员类型; - 若要将成员设为 “键成员”(用于创建主题实例),可设置
MemberDescriptor的is_key(是否为键)属性,其效果等同于使用内置注解@key; - 可通过
MemberDescriptor的default_value(默认值)属性设置成员的默认值,效果等同于使用内置注解@default; - 可通过
MemberDescriptor的id(ID)属性显式设置成员 ID,效果等同于使用内置注解@id。
注意:
- 当前 Fast DDS-Gen 暂不支持内置注解
@default;- 当前动态语言绑定 API 暂不支持以下内置注解:
@optional(可选)、@must_understand(必须理解)、@external(外部)、@try_construct(尝试构造)。
结构成员数据的管理方式:
- 使用与成员底层类型对应的访问器(获取器 / 设置器)操作成员数据;
- 通过
DynamicData::get_member_id_by_nameAPI 获取成员的MemberId; - 对于复杂类型的成员,请参考 “复杂类型数据的管理” 章节。
以下示例展示结构类型的定义与使用(含继承):
OMG-IDL 代码
// 定义内部结构体 InnerStruct
struct InnerStruct
{
@id(0x10) long first; // 成员 first,类型为 long,ID 为 0x10
};
// 定义父结构体 ParentStruct
struct ParentStruct
{
float first; // 成员 first,类型为 float
long long second; // 成员 second,类型为 long long
};
// 定义子结构体 ComplexStruct(继承自 ParentStruct)
struct ComplexStruct : ParentStruct
{
InnerStruct complex_member; // 成员 complex_member,类型为 InnerStruct
};
XML 代码
<struct name="InnerStruct">
<!-- XML does not support setting Member ID -->
<member name="first" type="int32"/>
</struct>
<!-- TODO(XTypes: Fix inheritance loading from XML profile) Fast DDS#4626 -->
<!-- <struct name="ParentStruct">
<member name="first" type="float32"/>
<member name="second" type="int64"/>
</struct>
<struct name="ComplexStruct" baseType="ParentStruct">
<member name="complex_member" type="nonBasic" nonBasicTypeName="InnerStruct"/>
</struct> -->
C++ 代码
// 1. 创建内部结构体类型 InnerStruct
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(eprosima::fastdds::dds::TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("InnerStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 向 InnerStruct 中添加成员 first
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(eprosima::fastdds::dds::TK_INT32)
); // 成员类型为 int32(对应 long)
member_descriptor->name("first"); // 成员名称
member_descriptor->id(16); // 成员 ID 设为 0x10(十六进制)
builder->add_member(member_descriptor);
// 构建 InnerStruct 类型
DynamicType::_ref_type inner_struct_type {builder->build()};
// 2. 创建父结构体类型 ParentStruct
TypeDescriptor::_ref_type parentstruct_type_descriptor {traits<TypeDescriptor>::make_shared()};
parentstruct_type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
parentstruct_type_descriptor->name("ParentStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type parentstruct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(parentstruct_type_descriptor)
};
// 向 ParentStruct 中添加成员 first(float 类型)
MemberDescriptor::_ref_type parentstruct_member {traits<MemberDescriptor>::make_shared()};
parentstruct_member->name("first");
parentstruct_member->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_FLOAT32)
); // 类型为 float32(对应 float)
parentstruct_builder->add_member(parentstruct_member);
// 向 ParentStruct 中添加成员 second(long long 类型)
parentstruct_member = traits<MemberDescriptor>::make_shared();
parentstruct_member->name("second");
parentstruct_member->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT64)
); // 类型为 int64(对应 long long)
parentstruct_builder->add_member(parentstruct_member);
// 构建 ParentStruct 类型
DynamicType::_ref_type parentstruct_type = parentstruct_builder->build();
// 3. 创建子结构体类型 ComplexStruct(继承自 ParentStruct)
TypeDescriptor::_ref_type complexstruct_type_descriptor {traits<TypeDescriptor>::make_shared()};
complexstruct_type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
complexstruct_type_descriptor->name("ComplexStruct"); // 设置结构体名称
complexstruct_type_descriptor->base_type(parentstruct_type); // 继承自 ParentStruct
DynamicTypeBuilder::_ref_type complexstruct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(complexstruct_type_descriptor)
};
// 向 ComplexStruct 中添加成员 complex_member(InnerStruct 类型)
MemberDescriptor::_ref_type complexstruct_member {traits<MemberDescriptor>::make_shared()};
complexstruct_member->name("complex_member");
complexstruct_member->type(inner_struct_type); // 类型为 InnerStruct
complexstruct_builder->add_member(complexstruct_member);
// 构建 ComplexStruct 类型
DynamicType::_ref_type complexstruct_type = complexstruct_builder->build();
// 基于 ComplexStruct 类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(complexstruct_type)};
// 为 float 类型的成员 first(继承自 ParentStruct)设置并获取值
float in_value {3.14}; // 待设置的值
float out_value {0.0}; // 用于存储获取到的值
data->set_float32_value(data->get_member_id_by_name("first"), in_value); // 设置值
data->get_float32_value(out_value, data->get_member_id_by_name("first")); // 获取值
有关该类型的 XML 定义详细说明,请参考 “XML 结构类型” 章节。
14.2.2.10 联合类型(Union Types)
联合类型是一种特殊的结构类型,同一时间仅存在一个成员。用于标识联合类型的 TypeKind 为 TK_UNION。
联合类型通过 “鉴别器”(discriminator)选择当前活跃的成员 —— 鉴别器的类型需通过 TypeDescriptor 的 discriminator_type(鉴别器类型)属性配置。支持的鉴别器 TypeKind 如下:
- 基本类型:
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(需直接或间接解析为上述类型之一)。
此外,可通过 TypeDescriptor 的 extensibility_kind(可扩展性种类)属性配置联合类型的可扩展性。
注意:当前暂不支持内置注解
@nested(嵌套)。
需通过 DynamicTypeBuilder 的 DynamicTypeBuilder::add_member 方法配置联合成员 —— 为每个成员调用该方法,并传入对应的 MemberDescriptor,且至少需添加一个联合成员。
联合成员的配置规则如下:
- 通过
MemberDescriptor的name(名称)属性设置成员名称; - 通过
MemberDescriptor的type(类型)属性设置成员类型; - 必须通过以下两种方式之一指定成员的 “选择条件”:
- 设置
MemberDescriptor的label(标签)属性:指定鉴别器的哪些值会选择该成员; - 设置
MemberDescriptor的is_default_label(是否为默认标签)属性:将该成员设为 “默认成员”(当鉴别器值不匹配任何标签时选择);
- 规则限制:仅允许一个成员被设为默认成员;若未配置
label,则必须将成员设为默认成员。
- 设置
- 可通过
MemberDescriptor的default_value(默认值)属性设置成员的默认值,效果等同于使用内置注解@default; - 可通过
MemberDescriptor的id(ID)属性显式设置成员 ID,效果等同于使用内置注解@id。
注意:
- 当前 Fast DDS-Gen 暂不支持内置注解
@default;- 当前动态语言绑定 API 暂不支持以下内置注解:
@optional(可选)、@must_understand(必须理解)、@external(外部)、@try_construct(尝试构造)。
联合成员数据的管理方式:
- 使用与成员底层类型对应的访问器(获取器 / 设置器)操作成员数据;
- 设置成员时,鉴别器值会自动更新为该成员对应的标签值;
- 读取成员时,鉴别器必须当前正选择该成员;
- 通过
DynamicData::get_member_id_by_nameAPI 获取成员的MemberId; - 对于复杂类型的成员,请参考 “复杂类型数据的管理” 章节。
以下示例展示联合类型的定义与使用(含嵌套联合):
OMG-IDL 代码
// 定义内部联合 InnerUnion(鉴别器类型为 short)
union InnerUnion switch (short)
{
case 0: // 鉴别器值为 0 时,选择成员 first
@id(0x10) PrimitivesStruct first; // 成员 first,类型为 PrimitivesStruct,ID 为 0x10
case 1: // 鉴别器值为 1 时,选择成员 second
default: // 鉴别器值不匹配时,默认选择成员 second
long long second; // 成员 second,类型为 long long
};
// 定义复杂联合 ComplexUnion(鉴别器类型为 long)
union ComplexUnion switch (long)
{
case 0: // 鉴别器值为 0 或 1 时,选择成员 third
case 1:
long third; // 成员 third,类型为 long
default: // 鉴别器值不匹配时,默认选择成员 fourth
InnerUnion fourth; // 成员 fourth,类型为 InnerUnion(嵌套联合)
};
XML 代码
<!-- 定义内部联合 InnerUnion -->
<union name="InnerUnion">
<discriminator type="int16"/> <!-- 鉴别器类型为 int16(对应 short) -->
<case>
<caseDiscriminator value="0"/> <!-- 鉴别器值为 0 -->
<!-- 成员 first,类型为非基本类型 PrimitivesStruct -->
<member name="first" type="nonBasic" nonBasicTypeName="PrimitivesStruct"/>
</case>
<case>
<caseDiscriminator value="1"/> <!-- 鉴别器值为 1 -->
<caseDiscriminator value="default"/> <!-- 默认情况 -->
<member name="second" type="int64"/> <!-- 成员 second,类型为 int64(对应 long long) -->
</case>
</union>
<!-- 定义复杂联合 ComplexUnion -->
<union name="ComplexUnion">
<discriminator type="int32"/> <!-- 鉴别器类型为 int32(对应 long) -->
<case>
<caseDiscriminator value="0"/> <!-- 鉴别器值为 0 -->
<caseDiscriminator value="1"/> <!-- 鉴别器值为 1 -->
<member name="third" type="int32"/> <!-- 成员 third,类型为 int32(对应 long) -->
</case>
<case>
<caseDiscriminator value="default"/> <!-- 默认情况 -->
<!-- 成员 fourth,类型为非基本类型 InnerUnion -->
<member name="fourth" type="nonBasic" nonBasicTypeName="InnerUnion"/>
</case>
</union>
C++ 代码
// 1. 创建内部联合类型 InnerUnion(鉴别器类型为 int16)
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_UNION); // 设置类型为联合
type_descriptor->name("InnerUnion"); // 设置联合名称
// 配置鉴别器类型为 int16(对应 short)
type_descriptor->discriminator_type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16)
);
DynamicTypeBuilder::_ref_type builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 向 InnerUnion 中添加成员 first(标签为 0,类型为 PrimitivesStruct)
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
member_descriptor->type(struct_type); // struct_type 为之前定义的 PrimitivesStruct 类型
member_descriptor->name("first");
member_descriptor->id(16); // 成员 ID 设为 0x10(十六进制)
member_descriptor->label({0}); // 鉴别器值为 0 时选择该成员
builder->add_member(member_descriptor);
// 向 InnerUnion 中添加成员 second(标签为 1,默认成员,类型为 int64)
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT64)
); // 类型为 int64(对应 long long)
member_descriptor->name("second");
member_descriptor->label({1}); // 鉴别器值为 1 时选择该成员
member_descriptor->is_default_label(true); // 设为默认成员
builder->add_member(member_descriptor);
// 构建 InnerUnion 类型
DynamicType::_ref_type inner_union_type {builder->build()};
// 2. 创建复杂联合类型 ComplexUnion(鉴别器类型为 int32)
type_descriptor = traits<TypeDescriptor>::make_shared();
type_descriptor->kind(TK_UNION); // 设置类型为联合
type_descriptor->name("ComplexUnion"); // 设置联合名称
// 配置鉴别器类型为 int32(对应 long)
type_descriptor->discriminator_type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32)
);
builder = DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor);
// 向 ComplexUnion 中添加成员 third(标签为 0、1,类型为 int32)
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT32)
); // 类型为 int32(对应 long)
member_descriptor->name("third");
member_descriptor->label({0, 1}); // 鉴别器值为 0 或 1 时选择该成员
builder->add_member(member_descriptor);
// 向 ComplexUnion 中添加成员 fourth(默认成员,类型为 InnerUnion)
member_descriptor = traits<MemberDescriptor>::make_shared();
member_descriptor->type(inner_union_type); // 类型为 InnerUnion
member_descriptor->name("fourth");
member_descriptor->is_default_label(true); // 设为默认成员
builder->add_member(member_descriptor);
// 构建 ComplexUnion 类型
DynamicType::_ref_type union_type {builder->build()};
// 基于 ComplexUnion 类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(union_type)};
// 借出 InnerUnion 类型成员(fourth)的数据引用
DynamicData::_ref_type union_data = data->loan_value(data->get_member_id_by_name("fourth"));
// 为 InnerUnion 中的 long long 类型成员 second 设置并获取值
int64_t in_value {2}; // 待设置的值
int64_t out_value; // 用于存储获取到的值
// 设置值(自动将鉴别器更新为 second 对应的标签)
union_data->set_int64_value(union_data->get_member_id_by_name("second"), in_value);
// 获取值
union_data->get_int64_value(out_value, union_data->get_member_id_by_name("second"));
// 归还借出的成员数据引用
data->return_loaned_value(union_data);
有关该类型的 XML 定义详细说明,请参考 “XML 联合类型” 章节。
14.2.2.11 位集类型(Bitset Types)
位集类型是位域(bitfield)的聚合。用于标识位集类型的 TypeKind 为 TK_BITSET。
TypeDescriptor 的 bound(边界)属性存储 “每个位域的位数(bitcount)序列”—— 为保证一致性,bound 序列的长度必须与位域的数量一致。
位集类型支持单继承—— 即一个位集类型可继承自另一个已定义的位集类型。被继承的位集类型需通过 TypeDescriptor 的 base_type(基础类型)属性配置。
需通过 DynamicTypeBuilder 的 DynamicTypeBuilder::add_member 方法配置位域 —— 为每个位域调用该方法,并传入对应的 MemberDescriptor,且至少需添加一个位域以保证位集类型的一致性。
位域的配置规则如下:
- 通过
MemberDescriptor的name(名称)属性设置位域名称; - 通过
MemberDescriptor的id(ID)属性设置位域的 “起始位位置”;- 对于继承的位集类型,第一个位域的起始位置必须在父位集类型已定义的位之后(避免位重叠);
- 每个位域独占一组位,不允许与其他位域重叠;
- 可通过
MemberDescriptor的type(类型)属性配置用于访问位域数据的整型类型;若未设置,则自动使用 “能容纳该位域位数的最小无符号整型”,具体规则如下表:
| 位域位数 | 对应的 C++ 存储类型 |
|---|---|
| 1 | bool |
| 2-8 | uint8_t |
| 9-16 | uint16_t |
| 17-32 | uint32_t |
| 33-64 | uint64_t |
每个位域(或位集成员)的使用方式与其对应的基本类型一致,唯一区别是:位域的内部存储仅修改其涉及的位,而非整个基本类型的值。
以下示例展示位集类型的定义与使用(含继承):
OMG-IDL 代码
// 定义父位集 ParentBitSet
bitset ParentBitSet
{
bitfield<3> a; // 位域 a,占 3 位
bitfield<1> b; // 位域 b,占 1 位
bitfield<4>; // 匿名位域,占 4 位
bitfield<10> c; // 位域 c,占 10 位
bitfield<12, short> d; // 位域 d,占 12 位,存储类型为 short
};
// 定义子位集 ChildBitSet(继承自 ParentBitSet)
bitset ChildBitSet : ParentBitSet
{
bitfield<1> e; // 位域 e,占 1 位
bitfield<20, unsigned long> f; // 位域 f,占 20 位,存储类型为 unsigned long
};
// 定义包含位集成员的结构体
struct BitsetStruct
{
ChildBitSet my_bitset; // 位集类型成员,类型为 ChildBitSet
};
XML 代码
<!-- 定义父位集 ParentBitSet -->
<bitset name="ParentBitSet">
<bitfield name="a" bit_bound="3"/> <!-- 位域 a,占 3 位 -->
<bitfield name="b" bit_bound="1"/> <!-- 位域 b,占 1 位 -->
<bitfield bit_bound="4"/> <!-- 匿名位域,占 4 位 -->
<bitfield name="c" bit_bound="10"/> <!-- 位域 c,占 10 位 -->
<bitfield name="d" bit_bound="12" type="int16"/> <!-- 位域 d,占 12 位,类型为 int16(对应 short) -->
</bitset>
<!-- 注:当前 XML 配置文件暂不支持加载位集继承(相关问题:Fast DDS#4626)-->
<!--
<bitset name="ChildBitSet" baseType="ParentBitSet">
<bitfield name="e" bit_bound="1"/> <!-- 位域 e,占 1 位 -->
<bitfield name="f" bit_bound="20" type="uint32"/> <!-- 位域 f,占 20 位,类型为 uint32(对应 unsigned long) -->
</bitset>
<struct name="BitsetStruct">
<!-- 成员 my_bitset,类型为非基本类型 ChildBitSet -->
<member name="my_bitset" type="nonBasic" nonBasicTypeName="ChildBitSet"/>
</struct>
-->
C++ 代码
// 1. 定义包含位集成员的结构体类型 BitsetStruct
TypeDescriptor::_ref_type struct_type_descriptor {traits<TypeDescriptor>::make_shared()};
struct_type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
struct_type_descriptor->name("BitsetStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type struct_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(struct_type_descriptor)
};
// 2. 定义父位集类型 ParentBitSet
TypeDescriptor::_ref_type bitset_type_descriptor {traits<TypeDescriptor>::make_shared()};
bitset_type_descriptor->kind(TK_BITSET); // 设置类型为位集
bitset_type_descriptor->name("ParentBitSet"); // 设置位集名称
bitset_type_descriptor->bound({3, 1, 10, 12}); // 位域位数序列(a:3, b:1, c:10, d:12)
DynamicTypeBuilder::_ref_type bitset_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(bitset_type_descriptor)
};
// 向 ParentBitSet 中添加位域 a(起始位 0)
MemberDescriptor::_ref_type bitset_member_descriptor {traits<MemberDescriptor>::make_shared()};
bitset_member_descriptor->name("a");
bitset_member_descriptor->id(0); // 起始位位置 0
bitset_builder->add_member(bitset_member_descriptor);
// 向 ParentBitSet 中添加位域 b(起始位 3,承接 a 的 3 位)
bitset_member_descriptor = traits<MemberDescriptor>::make_shared();
bitset_member_descriptor->name("b");
bitset_member_descriptor->id(3); // 起始位位置 3
bitset_builder->add_member(bitset_member_descriptor);
// 向 ParentBitSet 中添加位域 c(起始位 8,承接 b 的 1 位 + 匿名位域的 4 位)
bitset_member_descriptor = traits<MemberDescriptor>::make_shared();
bitset_member_descriptor->name("c");
bitset_member_descriptor->id(8); // 起始位位置 8
bitset_builder->add_member(bitset_member_descriptor);
// 向 ParentBitSet 中添加位域 d(起始位 18,承接 c 的 10 位,存储类型为 int16)
bitset_member_descriptor = traits<MemberDescriptor>::make_shared();
bitset_member_descriptor->name("d");
bitset_member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16)
); // 存储类型为 int16(对应 short)
bitset_member_descriptor->id(18); // 起始位位置 18
bitset_builder->add_member(bitset_member_descriptor);
// 构建 ParentBitSet 类型
DynamicType::_ref_type parentbitset_type = bitset_builder->build();
// 3. 定义子位集类型 ChildBitSet(继承自 ParentBitSet)
TypeDescriptor::_ref_type childbitset_type_descriptor {traits<TypeDescriptor>::make_shared()};
childbitset_type_descriptor->kind(TK_BITSET); // 设置类型为位集
childbitset_type_descriptor->name("ChildBitSet"); // 设置位集名称
childbitset_type_descriptor->base_type(parentbitset_type); // 继承自 ParentBitSet
childbitset_type_descriptor->bound({1, 20}); // 位域位数序列(e:1, f:20)
DynamicTypeBuilder::_ref_type childbitset_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(childbitset_type_descriptor)
};
// 向 ChildBitSet 中添加位域 e(起始位 30,承接 ParentBitSet 的总位数 30)
MemberDescriptor::_ref_type childbitset_member_descriptor {traits<MemberDescriptor>::make_shared()};
childbitset_member_descriptor->name("e");
childbitset_member_descriptor->id(30); // 起始位位置 30
childbitset_builder->add_member(childbitset_member_descriptor);
// 向 ChildBitSet 中添加位域 f(起始位 31,承接 e 的 1 位,存储类型为 uint32)
childbitset_member_descriptor = traits<MemberDescriptor>::make_shared();
childbitset_member_descriptor->name("f");
childbitset_member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_UINT32)
); // 存储类型为 uint32(对应 unsigned long)
childbitset_member_descriptor->id(31); // 起始位位置 31
childbitset_builder->add_member(childbitset_member_descriptor);
// 构建 ChildBitSet 类型
DynamicType::_ref_type bitset_type = childbitset_builder->build();
// 向结构体 BitsetStruct 中添加位集成员 my_bitset
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
member_descriptor->name("my_bitset");
member_descriptor->type(bitset_type); // 类型为 ChildBitSet
struct_builder->add_member(member_descriptor);
// 构建结构体类型
DynamicType::_ref_type struct_type {struct_builder->build()};
// 基于结构体类型创建动态数据
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(struct_type)};
// 借出位集成员 my_bitset 的数据引用
DynamicData::_ref_type bitset_data = data->loan_value(data->get_member_id_by_name("my_bitset"));
// 为位域 d(int16 类型)设置并获取值
int16_t in_value {2}; // 待设置的值
int16_t out_value; // 用于存储获取到的值
bitset_data->set_int16_value(bitset_data->get_member_id_by_name("d"), in_value); // 设置值
bitset_data->get_int16_value(out_value, bitset_data->get_member_id_by_name("d")); // 获取值
// 归还借出的位集数据引用
data->return_loaned_value(bitset_data);
有关该类型的 XML 定义详细说明,请参考 “XML 位集类型” 章节。
14.2.2.12 注解(Annotations)
注解用于为类型或类型成员添加额外的元数据信息,分为 “自定义注解” 和 “内置注解” 两类。
14.2.2.12.1 自定义注解(Custom Annotations)
可通过以下 API 为类型或类型成员添加自定义注解:
- 为类型添加注解:
DynamicTypeBuilder::apply_annotation; - 为类型成员添加注解:
DynamicTypeBuilder::apply_annotation_to_member。
自定义注解通过 AnnotationDescriptor(注解描述符)定义,该描述符包含两个核心属性:
type(类型):表示待应用注解的DynamicType(即注解本身的类型);value(值):注解参数的值。
自定义注解的定义规则
- 注解类型的标识:注解类型的
TypeKind为TK_ANNOTATION,注解名称通过TypeDescriptor的name(名称)属性设置。 - 注解参数的配置:
- 注解可包含任意数量的参数;
- 通过
DynamicTypeBuilder::add_member方法配置参数 —— 每个参数对应一个MemberDescriptor,其中name(名称)属性指定参数名,type(类型)属性指定参数类型; - 支持的参数类型仅包括:基本类型、字符串类型、枚举类型(暂不支持宽字符串作为参数类型)。
- 注解参数值的设置:通过
AnnotationDescriptor::set_value()方法设置参数值,需满足:- 传入的参数名必须与注解类型中定义的参数名一致;
- 参数值需转换为字符串形式传入。
注意:当前 XML 动态类型暂不支持自定义注解。
以下示例展示自定义注解的定义与应用:
OMG-IDL 代码
// 定义自定义注解 MyAnnotation(含参数 length)
@annotation MyAnnotation
{
short length; // 注解参数 length,类型为 short
};
// 为结构体 AnnotatedStruct 应用注解 MyAnnotation(参数 length=5)
@MyAnnotation(length = 5)
struct AnnotatedStruct
{
// 为结构体成员 string_var 应用注解 MyAnnotation(参数 length=10)
@MyAnnotation(length = 10) string string_var;
};
XML 代码
<!-- 注:XML 类型配置文件暂不支持定义或设置自定义注解 -->
C++ 代码
// 1. 定义待添加注解的结构体 AnnotatedStruct
TypeDescriptor::_ref_type type_descriptor {traits<TypeDescriptor>::make_shared()};
type_descriptor->kind(TK_STRUCTURE); // 设置类型为结构体
type_descriptor->name("AnnotatedStruct"); // 设置结构体名称
DynamicTypeBuilder::_ref_type type_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(type_descriptor)
};
// 向结构体中添加成员 string_var(无界字符串类型)
MemberDescriptor::_ref_type member_descriptor {traits<MemberDescriptor>::make_shared()};
member_descriptor->name("string_var");
member_descriptor->type(
DynamicTypeBuilderFactory::get_instance()->create_string_type(static_cast<uint32_t>(LENGTH_UNLIMITED))->build()
);
type_builder->add_member(member_descriptor);
// 2. 定义自定义注解类型 MyAnnotation(含参数 length)
AnnotationDescriptor::_ref_type annotation_descriptor {traits<AnnotationDescriptor>::make_shared()};
TypeDescriptor::_ref_type annotation_type {traits<TypeDescriptor>::make_shared()};
// 配置注解类型的基本信息
annotation_type->kind(TK_ANNOTATION); // 设置类型为注解
annotation_type->name("MyAnnotation"); // 注解名称
DynamicTypeBuilder::_ref_type annotation_builder {
DynamicTypeBuilderFactory::get_instance()->create_type(annotation_type)
};
// 向注解类型中添加参数 length(类型为 int16,对应 short)
MemberDescriptor::_ref_type annotation_parameter {traits<MemberDescriptor>::make_shared()};
annotation_parameter->name("length"); // 参数名
annotation_parameter->type(
DynamicTypeBuilderFactory::get_instance()->get_primitive_type(TK_INT16)
); // 参数类型
annotation_builder->add_member(annotation_parameter);
// 构建注解类型,并关联到注解描述符
annotation_descriptor->type(annotation_builder->build());
// 3. 为结构体 AnnotatedStruct 应用注解 MyAnnotation(参数 length=5)
annotation_descriptor->set_value("length", std::to_string(5)); // 设置参数值为 5
type_builder->apply_annotation(annotation_descriptor);
// 4. 为结构体成员 string_var 应用注解 MyAnnotation(参数 length=10)
annotation_descriptor = traits<AnnotationDescriptor>::make_shared(); // 重新创建注解描述符
annotation_descriptor->type(annotation_builder->build()); // 关联注解类型
annotation_descriptor->set_value("length", std::to_string(10)); // 设置参数值为 10
// 获取成员 string_var 的引用,为其添加注解
DynamicTypeMember::_ref_type member;
type_builder->get_member_by_name(member, "string_var");
type_builder->apply_annotation_to_member(member->get_id(), annotation_descriptor);
14.2.2.12.2 内置注解(Builtin Annotations)
除用户自定义注解外,DDS-XTypes 规范还定义了一组 “内置注解”,这些注解在前面的章节中已部分提及。
警告:当前暂不支持为联合的鉴别器添加注解。
下表汇总了动态语言绑定 API 支持的内置注解,包括对应的 API 配置方式及各功能模块的支持情况。完整的内置注解列表及其行为说明,请参考 “内置注解” 章节。
| 内置注解 | 动态语言绑定 API 配置方式 | 动态语言绑定支持 | XML 动态类型支持 | IDL 解析动态类型支持 |
|---|---|---|---|---|
@appendable | TypeDescriptor 的 extensibility_kind(可扩展性种类)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@bit_bound | - 位集类型:TypeDescriptor 的 bound(边界)属性- 枚举类型:TypeDescriptor 的 literal_type(常量类型)属性 | ✅ 支持 | ✅ 支持(枚举类型暂不可配置) | ✅ 支持 |
@default | MemberDescriptor 的 default_value(默认值)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@default_literal | MemberDescriptor 的 is_default_literal(是否为默认常量)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@extensibility | TypeDescriptor 的 extensibility_kind(可扩展性种类)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@external | MemberDescriptor 的 is_shared(是否为共享)属性 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
@final | TypeDescriptor 的 extensibility_kind(可扩展性种类)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@id | MemberDescriptor 的 id(ID)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@key / @Key | MemberDescriptor 的 is_key(是否为键)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@mutable | TypeDescriptor 的 extensibility_kind(可扩展性种类)属性 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
@nested | TypeDescriptor 的 is_nested(是否为嵌套)属性 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
@optional | MemberDescriptor 的 is_optional(是否为可选)属性 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
@position | MemberDescriptor 的 position(位置)属性 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
@try_construct | MemberDescriptor 的 try_construct_kind(尝试构造种类)属性 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
@value | MemberDescriptor 的 literal_value(常量值)属性 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
@verbatim | VerbatimTextDescriptor(原始文本描述符) | ❌ 不支持 | ❌ 不支持 | ❌ 不支持 |
14.2.2.13 复杂类型数据的管理(Managing Complex Types Data)
部分 DynamicData 实例管理的 “复杂类型”(如嵌套结构体、联合、映射等)无法通过基本类型的获取器 / 设置器直接修改。动态语言绑定提供两种管理复杂类型数据的方式:
方式 1:基于复制的 API(get_complex_value / set_complex_value)
- 功能:通过
DynamicData::get_complex_value获取复杂类型的DynamicData副本,修改后通过DynamicData::set_complex_value将副本写回原数据; - 核心特点:始终执行数据复制,操作简单但可能存在性能开销(尤其对于大型数据)。
方式 2:基于引用的 API(loan_value / return_loaned_value)
- 功能:通过
DynamicData::loan_value借出复杂类型数据的引用,直接操作引用以避免复制;操作完成后必须通过DynamicData::return_loaned_value归还引用; - 核心特点:无数据复制,性能高效;但需注意 —— 对已借出的引用再次调用
loan_value会失败。
以下代码片段展示如何使用上述两种方式管理复杂类型数据(以 “结构类型” 章节中定义的 ComplexStruct 为例):
// 基于 ComplexStruct 类型创建动态数据(ComplexStruct 含嵌套成员 complex_member)
DynamicData::_ref_type data {DynamicDataFactory::get_instance()->create_data(complexstruct_type)};
// 方式 1:使用 get_complex_value / set_complex_value(复制方式)
DynamicData::_ref_type complex_data; // 用于存储复杂类型数据的副本
// 1. 获取 complex_member 的副本
data->get_complex_value(complex_data, data->get_member_id_by_name("complex_member"));
// 2. 修改副本中的数据(设置 complex_member 的成员 first 为 10)
int32_t in_value {10};
complex_data->set_int32_value(complex_data->get_member_id_by_name("first"), in_value);
// 3. 将修改后的副本写回原数据
data->set_complex_value(data->get_member_id_by_name("complex_member"), complex_data);
// 方式 2:使用 loan_value / return_loaned_value(引用方式)
// 1. 借出 complex_member 的引用(无复制)
DynamicData::_ref_type loan_data = data->loan_value(data->get_member_id_by_name("complex_member"));
// 2. 直接通过引用修改数据(操作与方式 1 一致,但无复制开销)
loan_data->set_int32_value(loan_data->get_member_id_by_name("first"), in_value);
// 3. 操作完成后,归还引用
data->return_loaned_value(loan_data);
14.3. 序列化工具
Fast DDS 提供了用于序列化 XTypes 对象的方法,能够在分布式系统内实现高效的数据交换与处理。序列化是数据分发服务(Data Distribution Service,DDS)中的关键流程,它可将复杂的数据结构转换为一种易于在不同平台和编程环境间传输与重构的格式。
14.3.1. 动态类型(Dynamic Type)转 IDL
idl_serialize 方法可将 DynamicType 对象序列化为其 IDL 表示形式。
注意事项
- 转换为 IDL 时,仅支持以下内置注解:
@bit_bound、@extensibility、@key和@position。 - 警告:带继承关系的位集(Bitset)转换为 IDL 时,会将派生位集与其基位集合并。
- 警告:转换为 IDL 时,会忽略显式设置为默认值的值。例如,位掩码(Bitmask)的默认
@bit_bound值为 32。若用户将位掩码的@bit_bound值显式设置为 32,随后将DynamicType序列化为 IDL,那么生成的 IDL 中将不包含@bit_bound注解。
14.3.1.1. 示例:将发现的类型转换为 IDL 格式
以下示例展示了如何在 Fast DDS 中使用 idl_serialize 方法,将发现的类型转换为 IDL 格式。每当订阅者发现新的 DataReader 或 DataWriter 时,会使用 DynamicTypeBuilderFactory 构建一个 DynamicType,并将其序列化为 IDL 格式。有关如何实现远程类型发现的更多细节,请参考 “远程类型发现与端点匹配” 章节。
/* 自定义回调函数 on_data_reader_discovery */
void on_data_reader_discovery(
DomainParticipant* /* participant */,
eprosima::fastdds::rtps::ReaderDiscoveryStatus /* reason */,
const eprosima::fastdds::dds::SubscriptionBuiltinTopicData& info,
bool& /* should_be_ignored */) override
{
// 获取远程类型信息
xtypes::TypeObject remote_type_object;
if (RETCODE_OK != DomainParticipantFactory::get_instance()->type_object_registry().get_type_object(
info.type_information.type_information.complete().typeid_with_size().type_id(),
remote_type_object))
{
// 错误处理
return;
}
// 构建远程发现的类型
DynamicType::_ref_type remote_type = DynamicTypeBuilderFactory::get_instance()->create_type_w_type_object(
remote_type_object)->build();
// 将 DynamicType 序列化为 IDL 表示形式
std::stringstream idl;
if (RETCODE_OK != idl_serialize(remote_type, idl))
{
// 错误处理
return;
}
// 打印 IDL 表示形式
std::cout << "发现的类型:\n" << idl.str() << std::endl;
}
/* 自定义回调函数 on_data_writer_discovery */
void on_data_writer_discovery(
DomainParticipant* /* participant */,
eprosima::fastdds::rtps::WriterDiscoveryStatus /*reason*/,
const eprosima::fastdds::dds::PublicationBuiltinTopicData& info,
bool& /* should_be_ignored */) override
{
// 获取远程类型信息
xtypes::TypeObject remote_type_object;
if (RETCODE_OK != DomainParticipantFactory::get_instance()->type_object_registry().get_type_object(
info.type_information.type_information.complete().typeid_with_size().type_id(),
remote_type_object))
{
// 错误处理
return;
}
// 构建远程发现的类型
DynamicType::_ref_type remote_type = DynamicTypeBuilderFactory::get_instance()->create_type_w_type_object(
remote_type_object)->build();
// 将 DynamicType 序列化为 IDL 表示形式
std::stringstream idl;
if (RETCODE_OK != idl_serialize(remote_type, idl))
{
// 错误处理
return;
}
// 打印 IDL 表示形式
std::cout << "发现的类型:\n" << idl.str() << std::endl;
}
14.3.2. 动态数据(DynamicData)转 JSON
在 DDS(数据分发服务)的场景中,DynamicType 表示在系统内分发的数据的结构。每个 DynamicData 对象都对应一个由其 DynamicType 表示的类型的对象,提供了访问和修改数据值的功能。为提高互操作性和可读性,通常会将 DynamicData 序列化为更易于处理的格式,以便在不同系统和应用间更便捷地进行数据处理与分析。json_serialize 方法可将 DynamicData 对象转换为 JSON 对象,随后输出到 std::ostream 中。反之,也可使用 json_deserialize 方法实现反向转换,具体内容将在对应的章节中介绍。
14.3.2.1. 支持的类型
本节介绍所有支持的类型对应的 DynamicData 到 JSON 输出流(ostream)的序列化方式。
14.3.2.1.1. 基本类型(Primitives)
基本类型是构成每个 DynamicType 的基础单元。以下是 IDL 中基本类型的定义示例:
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;
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"my_bool": false,
"my_octet": 0,
"my_char": "\u0000",
"my_wchar": "\u0000",
"my_long": 0,
"my_ulong": 0,
"my_int8": 0,
"my_uint8": 0,
"my_short": 0,
"my_ushort": 0,
"my_longlong": 0,
"my_ulonglong": 0,
"my_float": 0.0,
"my_double": 0.0,
"my_longdouble": 0.0
}
14.3.2.1.2. 字符串类型(Strings)
字符串类型用于表示字符序列,是系统内处理文本数据的关键类型。以下是 IDL 中字符串类型的定义示例:
struct StringsStruct
{
string my_string;
wstring my_wstring;
string<41925> my_bounded_string;
wstring<20925> my_bounded_wstring;
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"my_string": "",
"my_wstring": "",
"my_bounded_string": "",
"my_bounded_wstring": ""
}
14.3.2.1.3. 枚举类型(Enumerations)
枚举类型表示一组固定的命名值,便于处理预定义的选项列表。以下是 IDL 中枚举类型的定义示例:
enum MyEnum
{
A,
B,
C
};
struct EnumStruct
{
MyEnum my_enum;
};
上述 DynamicData 对象支持两种序列化格式:eProsima 格式和 OMG 格式,可根据所需的互操作性以及与其他系统的兼容性灵活选择。两种格式对应的序列化结果如下:
- eProsima 格式
{ "my_enum": { "name": "A", "value": 0 } } - OMG 格式
{ "my_enum": "A" }
14.3.2.1.4. 位掩码类型(Bitmasks)
位掩码类型可在单个值中表示一组标志,便于紧凑地存储多个布尔选项。以下是 IDL 中位掩码类型的定义示例:
@bit_bound(8) bitmask MyBitMask
{
@position(0) flag0,
flag1,
flag2,
@position(5) flag5
};
struct BitmaskStruct
{
MyBitMask my_bitmask;
};
位掩码在 eProsima 和 OMG 两种格式下也有不同的序列化结构。上述 DynamicData 对象在两种格式下的序列化结果如下:
- eProsima 格式
{ "my_bitmask": { "active": [], "binary": "00000000", "value": 0 } } - OMG 格式
{ "my_bitmask": 0 }
14.3.2.1.5. 序列类型(Sequences)
序列类型用于表示有序的元素集合,与数组类似,但长度可动态变化。以下是 IDL 中序列类型的定义示例:
struct SequenceStruct
{
sequence<MyBitMask> bitmask_sequence;
sequence<short, 5> short_sequence;
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"bitmask_sequence": [],
"short_sequence": []
}
14.3.2.1.6. 数组类型(Arrays)
数组类型用于表示固定大小的元素集合。以下是 IDL 中数组类型的定义示例:
struct ArrayStruct
{
long long_array[2][3];
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"long_array": [
[
0,
0,
0
],
[
0,
0,
0
]
]
}
14.3.2.1.7. 映射类型(Maps)
映射类型表示键值对集合,可根据唯一键高效查找对应的值。以下是 IDL 中映射类型的定义示例:
struct MapStruct
{
map<string, MyAliasedBoundedString> string_alias_unbounded_map;
map<short, long, 2> short_long_map;
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"short_long_map": null,
"string_alias_unbounded_map": null
}
14.3.2.1.8. 结构类型(Structures)
结构类型用于将不同类型的数据组合在一起。以下是 IDL 中结构类型的定义示例:
struct InnerStruct
{
@id(0x10) long first;
};
struct ParentStruct
{
float first;
long long second;
};
struct ComplexStruct : ParentStruct
{
InnerStruct complex_member;
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"complex_member": {
"first": 0
},
"first": 0.0,
"second": 0
}
14.3.2.1.9. 联合类型(Unions)
联合类型是一种特殊的结构类型,其中仅包含一个成员。以下是 IDL 中联合类型的定义示例:
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;
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"inner_union": {
"second": 0
},
"complex_union": {
"fourth": {
"second": 0
}
}
}
14.3.2.1.10. 位集类型(Bitsets)
位集类型是一种特殊的结构类型,可对单个位进行访问和操作。以下是 IDL 中位集类型的定义示例:
bitset MyBitSet
{
bitfield<3> a;
bitfield<1> b;
bitfield<4>;
bitfield<10> c;
bitfield<12, short> d;
};
struct BitsetStruct
{
MyBitSet my_bitset;
};
与上述类型对应的 DynamicData 对象序列化后如下:
{
"my_bitset": {
"a": 0,
"b": 0,
"c": 0,
"d": 0
}
}
14.3.2.2. 示例:将接收的数据转换为 JSON 格式
以下代码展示了如何在 Fast DDS 中使用 json_serialize 函数,将接收的数据序列化为更易于处理和理解的 JSON 格式。每当订阅者接收到新数据时,可从 DynamicDataFactory 中获取对应的 DynamicData,并将其序列化为 JSON 字符串格式。有关如何实现远程类型发现的更多细节,请参考 “远程类型发现与端点匹配” 章节。
void on_data_available(
DataReader* reader)
{
// 使用从发现的 TypeObject 或通过动态语言绑定 API 创建的 DynamicType 来创建数据
DynamicData::_ref_type new_data =
DynamicDataFactory::get_instance()->create_data(dyn_type_serialization_);
SampleInfo info;
while ((RETCODE_OK == reader->take_next_sample(&new_data, &info)))
{
std::stringstream output;
output << std::setw(4);
// 将 DynamicData 序列化为 JSON 字符串格式
if (RETCODE_OK != json_serialize(
new_data,
DynamicDataJsonFormat::EPROSIMA,
output))
{
// 错误处理
return;
}
// 打印 JSON 表示形式
std::cout << "收到的消息:\n" << output.str() << std::endl;
}
}
// 与接收数据对应的 DynamicType
DynamicType::_ref_type dyn_type_serialization_;
14.3.3. JSON 转动态数据(DynamicData)
除了支持将 DynamicData 序列化为 JSON 外,Fast DDS 还提供了反向转换的方式。json_deserialize 方法仅需传入 DynamicData 关联的 DynamicType,即可将 JSON 对象转换为 DynamicData 实例。该方法可用于将外部来源的数据注入到 DDS 网络中,从而实现不同系统和应用间的数据集成。
14.3.3.1. 示例:将 JSON 数据转换为 DynamicData
以下代码展示了如何在 Fast DDS 中使用 json_deserialize 函数,将 JSON 数据转换为 DynamicData 对象。
void json_to_data(
const std::string& json_data)
{
// 将 JSON 字符串反序列化为 DynamicData
// (注:原文此处代码不完整,未展示完整的反序列化逻辑)
}
14.4. 动态类型 IDL 解析(Dynamic Types IDL Parsing)
Fast DDS 支持通过动态语言绑定(Dynamic Language Binding)机制,在运行时解析 IDL(接口定义语言)文件以生成动态数据类型。
这一特性允许应用程序从 IDL 文件中动态加载类型定义,而无需完全依赖预生成的类型或 XML 配置文件。该功能提升了应用灵活性,新增数据类型时无需修改应用代码并重新编译即可直接使用。
14.4.1. 支持的类型
下文列出了 eProsima Fast DDS IDL 解析功能所支持的类型。有关动态语言绑定支持类型的更多详细信息,请参考 “支持的类型”(Supported Types)章节。
支持的类型
- 基本类型(Primitive Types)
- 字符串类型(String Types)
- 结构类型(Structure Types)
- 别名类型(Alias Types)
- 数组类型(Array Types)
- 序列类型(Sequence Types)
- 联合类型(Union Types)
- 枚举类型(Enumeration Types)
- 算术表达式(Arithmetic Expressions)
- 联合 / 结构前向声明(Union/struct Forward Declarations)
- 模块 / 作用域类型(Modules/Scoped Types)
- 内置注解(Builtin Annotations)
暂不支持的类型
目前 IDL 解析功能暂不支持以下类型:
- 位掩码类型(Bitmask Types)
- 映射类型(Map Types)
- 位集类型(Bitset Types)
- 自定义注解(Custom Annotations)
- 继承(Inheritance)
- 成员 ID(Member ID)
14.4.2. 从 IDL 文件创建动态类型
Fast DDS 应用程序可通过以下方式使用运行时生成的动态类型:调用 DynamicTypeBuilderFactory::create_type_w_uri 方法,将对应的 IDL 文件加载到 DynamicTypeBuilderFactory 中。获取动态类型(DynamicType)后,可实例化 DynamicPubSubType 类的对象,并用于数据的写入与读取操作。
警告
DynamicTypeBuilderFactory::create_type_w_uri 方法需要传入 IDL 文件中定义的类型的全限定名。若未指定作用域(scope),则默认使用全局作用域。
注意
可通过 DynamicTypeBuilderFactory::set_preprocessor 方法手动选择预处理器。
14.4.2.1. 示例
以下代码片段展示了上述步骤的具体实现:
// 在指定域中创建 DomainParticipant(域参与者)
DomainParticipant* participant =
DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT);
if (nullptr == participant)
{
// 错误处理
return;
}
// (可选)设置解析 IDL 前要执行的预处理器
DynamicTypeBuilderFactory::get_instance()->set_preprocessor("<预处理器可执行文件的可选路径>");
// 加载 IDL 文件
std::string idl_file = "<IDL 文件路径>.idl";
std::string type_name = "YourType"; // IDL 中定义的类型名
std::vector<std::string> include_paths;
include_paths.push_back("<包含被引用 IDL 文件的文件夹路径>");
// 获取目标类型的实例
DynamicTypeBuilder::_ref_type dyn_type_builder =
DynamicTypeBuilderFactory::get_instance()->create_type_w_uri(idl_file, type_name, include_paths);
// 注册动态类型
TypeSupport dyn_type_support(new DynamicPubSubType(dyn_type_builder->build()));
dyn_type_support.register_type(participant, nullptr);
// 使用已注册的类型创建 Topic(主题)
Topic* topic =
participant->create_topic("topic_name", dyn_type_support.get_type_name(), TOPIC_QOS_DEFAULT);
if (nullptr == topic)
{
// 错误处理
return;
}
14.4.3. 遍历已解析的 IDL 类型
Fast DDS 应用程序支持通过简单方式遍历 IDL 文件中声明并解析后的所有聚合类型(aggregated type),包括:
- 联合类型(Union Types)
- 枚举类型(Enumeration Types)
- 结构类型(Structure Types)
- 别名类型(Alias Types)
实现方式为调用 DynamicTypeBuilderFactory::for_each_type_w_uri 方法。用户可通过提供 ** lambda 表达式 ** 自定义运行时行为 —— 每当解析到一个新的聚合类型时,该 lambda 表达式会被触发执行。
用户提供的回调函数会接收与已解析类型关联的 DynamicTypeBuilder 实例,并返回一个布尔值:
- 返回
true:继续遍历后续类型 - 返回
false:停止解析过程,不再处理后续类型
14.4.3.1. 示例
以下代码片段展示了如何遍历 IDL 文件中声明的聚合类型,并将其名称存储到向量(vector)中:
// (可选)设置解析 IDL 前要执行的预处理器
DynamicTypeBuilderFactory::get_instance()->set_preprocessor("<预处理器可执行文件的可选路径>");
// 加载 IDL 文件
std::string idl_file = "<IDL 文件路径>.idl";
std::vector<std::string> include_paths;
include_paths.push_back("<包含被引用 IDL 文件的文件夹路径>");
// 用于存储已解析类型名称的向量
std::vector<std::string> parsed_types;
// 定义回调函数:对 IDL 中解析到的每个聚合类型执行
auto callback = [&parsed_types](traits<DynamicTypeBuilder>::ref_type builder)
{
if (builder) // 检查 builder 是否有效
{
parsed_types.emplace_back(builder->get_name().to_string()); // 将类型名存入向量
}
return true; // 返回 true,继续解析后续类型
};
// 遍历 IDL 中的聚合类型,将名称存入向量
DynamicTypeBuilderFactory::get_instance()->for_each_type_w_uri(idl_file, include_paths, callback);

&spm=1001.2101.3001.5002&articleId=153988041&d=1&t=3&u=16c0732e100c4096b00aa8d41dbb1048)
3067

被折叠的 条评论
为什么被折叠?



