4. 嵌套结构体消息
任务目标
在FastDDS中定义包含嵌套结构体的自定义消息类型,并基于该类型完成一个发布-订阅通信闭环。本案例将定义Address结构体(包含街道、城市)与Person结构体(包含姓名、年龄、地址),通过FastDDS的IDL编译器生成类型支持代码,实现一个发布者周期发送Person消息、订阅者接收并打印的完整示例。
环境与依赖
- FastDDS ≥ 2.6(含
fastddsgen工具) - C++11及以上编译器,CMake ≥ 3.16
- 需安装FastDDS Python绑定(用于
fastddsgen)或使用FastDDS提供的IDL编译器
实现步骤
整体分为三步:编写IDL定义嵌套类型 → 使用fastddsgen生成C++类型支持代码 → 编写发布订阅主程序并编译运行。
1. 定义IDL文件(person.idl)
struct Address {
string street;
string city;
uint16 zip_code;
};
struct Person {
string name;
uint16 age;
Address address;
};
2. 生成类型支持代码
fastddsgen person.idl
该命令生成person.h、person.cxx、PersonPubSubTypes.h、PersonPubSubTypes.cxx等文件,其中包含嵌套类型的序列化/反序列化实现。
3. 发布订阅主程序(main.cpp)
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>
#include <fastdds/rtps/transport/TCPv4TransportDescriptor.hpp>
#include <thread>
#include <chrono>
#include <iostream>
#include "personPubSubTypes.h"
using namespace eprosima::fastdds::dds;
class SubListener : public DataReaderListener {
public:
void on_data_available(DataReader* reader) override {
Person sample;
SampleInfo info;
while (reader->take_next_sample(&sample, &info) == ReturnCode_t::RETCODE_OK) {
if (info.valid_data) {
std::cout << "Received: " << sample.name() << ", age=" << sample.age()
<< ", addr=" << sample.address().street() << " "
<< sample.address().city() << " " << sample.address().zip_code() << std::endl;
}
}
}
};
int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " pub|sub" << std::endl;
return 1;
}
bool is_pub = (std::string(argv[1]) == "pub");
DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT;
DomainParticipant* participant = DomainParticipantFactory::get_instance().create_participant(0, pqos);
if (!participant) return 1;
TypeSupport type(new PersonPubSubType());
type.register_type(participant);
Topic* topic = participant->create_topic("PersonTopic", "Person", TOPIC_QOS_DEFAULT);
if (!topic) return 1;
if (is_pub) {
Publisher* pub = participant->create_publisher(PUBLISHER_QOS_DEFAULT);
DataWriter* writer = pub->create_datawriter(topic, DATAWRITER_QOS_DEFAULT);
if (!writer) return 1;
Person msg;
msg.name("Alice");
msg.age(30);
msg.address().street("123 Main St");
msg.address().city("Springfield");
msg.address().zip_code(12345);
for (int i = 0; i < 10; ++i) {
msg.age(30 + i);
writer->write(&msg);
std::cout << "Sent: " << msg.name() << " age=" << msg.age() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
} else {
Subscriber* sub = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT);
SubListener listener;
DataReader* reader = sub->create_datareader(topic, DATAREADER_QOS_DEFAULT, &listener);
if (!reader) return 1;
std::this_thread::sleep_for(std::chrono::seconds(6));
}
participant->delete_contained_entities();
DomainParticipantFactory::get_instance().delete_participant(participant);
return 0;
}
4. CMake配置(CMakeLists.txt)
cmake_minimum_required(VERSION 3.16)
project(nested_dds)
find_package(fastdds REQUIRED)
find_package(fastcdr REQUIRED)
add_executable(nested_pubsub main.cpp person.cxx personPubSubTypes.cxx)
target_link_libraries(nested_pubsub fastdds fastcdr)
运行与验证
# 编译
mkdir build && cd build
cmake ..
make
# 终端1:启动订阅者
./nested_pubsub sub
# 终端2:启动发布者
./nested_pubsub pub
通过条件:订阅者终端依次打印出10条包含姓名、年龄、街道、城市和邮编的完整消息,且年龄从30递增到39,即证明嵌套结构体的序列化、传输和反序列化正确。
关键说明
- 嵌套结构体在IDL中直接使用成员变量声明,
fastddsgen自动生成对应的访问方法(如address()返回引用),无需手动编写序列化代码。 - 发布者与订阅者必须使用相同的Domain ID(此处为0)和Topic名称(
PersonTopic),且类型名Person需与IDL定义一致。 - 若需跨机器通信,需配置发现协议或使用TCP传输,本案例默认使用共享内存/UDP本地通信。