ROS2中如何创建可存储不同类型消息的std::map?
ROS2中用std::map关联话题名与不同类型消息的正确声明方式
你的初始写法不可行
你尝试的代码无法正常工作,因为std::map要求所有元素的值类型必须完全一致。不同ROS2消息类型生成的tuple是完全不同的类型(比如tuple<msgType1, msgType1::SharedPtr>和tuple<msgType2, msgType2::SharedPtr>属于两种独立类型),编译器会拒绝将不同类型的tuple存入同一个std::map。
ROS2消息类型的基类情况
ROS2的所有消息类型都继承自rclcpp::MessageBase,对应的MsgType::SharedPtr本质是std::shared_ptr<MsgType>,可以向上转型为std::shared_ptr<rclcpp::MessageBase>。但要注意:
- 如果你想存储消息对象本身(比如
msg1_t这种栈实例),无法用基类对象统一存储——因为直接存基类对象会触发对象切片,丢失派生类的特有数据,所以这种场景必须用类型擦除机制。 - 如果你只需要存储消息的共享指针,那么
std::shared_ptr<rclcpp::MessageBase>可以作为统一的指针类型。
可行的实现方案
方案1:存储消息对象+共享指针(用std::any做类型擦除)
如果必须同时存储消息实例和其共享指针,可以用std::any来实现类型擦除,让不同类型的消息对象能存入同一个tuple:
#include <any> #include <map> #include <string> #include <tuple> #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/String.hpp" #include "sensor_msgs/msg/Image.hpp" // 声明map:键是话题名,值是(消息对象, 消息共享指针)的tuple std::map<std::string, std::tuple<std::any, std::shared_ptr<rclcpp::MessageBase>>> ref_maps; int main() { // 存入std_msgs::String类型的消息 std_msgs::msg::String str_msg; str_msg.data = "test string"; auto str_ptr = std::make_shared<std_msgs::msg::String>(str_msg); ref_maps["topic_string"] = std::make_tuple(str_msg, str_ptr); // 存入sensor_msgs::Image类型的消息 sensor_msgs::msg::Image img_msg; // 填充图像消息的必要字段(比如width、height等) auto img_ptr = std::make_shared<sensor_msgs::msg::Image>(img_msg); ref_maps["topic_image"] = std::make_tuple(img_msg, img_ptr); // 取出并使用消息 auto [stored_str_msg, stored_str_ptr] = ref_maps["topic_string"]; // 必须明确指定类型进行转换,转换失败会抛出异常 auto& actual_str = std::any_cast<std_msgs::msg::String&>(stored_str_msg); auto actual_str_ptr = std::dynamic_pointer_cast<std_msgs::msg::String>(stored_str_ptr); if (actual_str_ptr) { RCLCPP_INFO(rclcpp::get_logger("test"), "String data: %s", actual_str.data.c_str()); } return 0; }
方案2:仅存储共享指针(更简洁常用)
如果不需要存储消息对象本身,只存共享指针的话,实现会更简洁,避免类型擦除的复杂度:
#include <map> #include <string> #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/String.hpp" #include "sensor_msgs/msg/Image.hpp" // 声明map:键是话题名,值是统一的消息基类共享指针 std::map<std::string, std::shared_ptr<rclcpp::MessageBase>> ref_maps; int main() { // 存入消息指针 auto str_ptr = std::make_shared<std_msgs::msg::String>(); str_ptr->data = "test string"; ref_maps["topic_string"] = str_ptr; auto img_ptr = std::make_shared<sensor_msgs::msg::Image>(); // 填充图像字段 ref_maps["topic_image"] = img_ptr; // 取出并使用 auto str_ptr_out = std::dynamic_pointer_cast<std_msgs::msg::String>(ref_maps["topic_string"]); if (str_ptr_out) { RCLCPP_INFO(rclcpp::get_logger("test"), "String data: %s", str_ptr_out->data.c_str()); } return 0; }
注意事项
- 使用
std::dynamic_pointer_cast转型时,一定要检查返回值是否为空,避免空指针访问。 - 如果用
std::any存储消息对象,取出时必须准确匹配原始类型,否则会抛出std::bad_any_cast异常,建议结合std::any::type()做类型校验后再转换。
内容的提问来源于stack exchange,提问作者furynerd
相关产品推荐
相关产品推荐

