You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:46:21