• 【ROS2原理11】C++编程的要点


    一、关于命名空间

            ROS 包的所有代码都应定义在以包命名的命名空间中。为了将生成的代码与包中的其他代码分开,它在子命名空间中定义:

    • ROS messages上的命名空间:::msg.
    • ROS services上的命名空间:::srv.

            注意:使用额外的子命名空间可确保符号不同且不与 ROS 1 符号重叠。这允许将两者都包含在像 ros1_bridge 这样的单个编译单元中。

    二、生成的文件

            遵循 ROS 2 的 C++ 风格指南,命名空间层次结构映射到文件夹结构。文件名使用带有下划线的小写字母数字字符来分隔单词,并以 .hpp 或 .cpp 结尾。

    2.1 消息

            对于消息,会生成一个同名后跟下划线的模板化结构。单个模板参数是数据结构的分配器。        

            为了便于使用,有一个与消息同名的 typedef,它使用默认分配器(例如 std::allocator)。

    对于每条消息,正在生成两个文件:

    • .hpp 目前仅包含 __struct.hpp
    • __struct.hpp 包含结构的定义

             除了带有后缀 __struct 的文件之外,这还允许添加其他文件以提供附加功能。对于每个附加功能,可以决定从第一个头文件中包含它。

    TODO: specify content of __traits.hpp file

    三、数据的类型Types

    3.1 主要数据类型:

    ROS typeC++ type
    boolbool
    byteuint8_t
    charchar
    float32float
    float64double
    int8int8_t
    uint8uint8_t
    int16int16
    uint16uint16
    int32int32
    uint32uint32
    int64int64
    uint64uint64_t
    stringstd::string

    3.2 数组和有界字符串的映射

    ROS typeC++ type
    static arraystd::array
    unbounded dynamic arraystd::vector
    bounded dynamic arraycustom_class
    bounded stringstd::string

    四、类成员(Members)

            该结构对消息的每个字段都有同名的公共成员变量。对于每个字段,都会创建一个 typedef,它以具有前导下划线和尾随 _type 的成员命名。

    4.1 常量成员

            数值常量被定义为结构中的枚举。

            所有其他常量在结构中声明为静态 const 成员,并且它们的值在结构之外定义。

    4.2 构造函数

            这里的默认指定:
            在下面的讨论中,“member”指的是 C++ 类中的类成员,而“field”指的是 IDL 文件中的字段定义。

            默认构造函数使用 IDL 文件中指定的默认值初始化所有成员,或者使用本文中定义的字段类型的通用默认值初始化所有成员(注意:对于 C++,char 字段被视为数字)。在某些情况下,这可能是不可取的,因为这些字段通常会立即被用户提供的值覆盖。因此,构造函数采用 rosidl_generator_cpp::MessageInitialization 类型的可选指令来控制初始化的完成方式:

    • MessageInitialization::ALL - 使用 IDL 文件中指定的字段默认值初始化每个成员,或者使用本文中定义的字段类型的通用默认值初始化每个成员(注意:char 字段被认为是 C++ 的数字)。
      • 最安全的选项,也是默认选项(如果不向构造函数传递任何参数,则使用)。
    • MessageInitialization::SKIP - 不初始化任何成员;用户有责任确保所有成员都被初始化为某个值,否则可能会导致未定义的行为
      • 如果用户自己设置所有成员,则用于获得最佳性能。
    • MessageInitialization::ZERO - 零初始化所有成员;所有成员都将被初始化(动态大小或上边界数组将有零个元素),消息定义中的默认值将被忽略
      • 当用户不希望初始化潜在复杂或较大的默认值的开销,但仍希望确保所有变量都正确初始化时使用。
    • MessageInitialization::DEFAULTS_ONLY - 仅初始化具有字段默认值的成员;所有其他成员将保持未初始化状态
      • 最低限度的初始化确保现有代码在稍后将具有默认值的新字段添加到 IDL 时正确初始化成员。

            可以选择使用分配器调用构造函数。

            该结构没有带有成员位置参数的构造函数。这样做的简短原因是,如果代码依赖位置参数来构造数据对象,更改消息定义会以微妙的方式破坏现有代码。由于这会阻碍消息定义的演变,因此应通过单独设置成员来填充数据结构,例如使用 setter 方法。

    4.3 设定函数(Setters)

            对于每个字段,都会生成一个 setter 方法以启用方法链接。它们以带有前导集__的字段命名。 setter 方法有一个参数来传递成员变量的值。每个 setter 方法都返回结构本身。

    4.4 操作(Operators)

            比较运算符 == 和 != 基于每个成员执行比较。

    4.5 指针类型(Pointer types)

            该结构包含四种常见指针类型的 typedef,即普通指针、std::shared_ptr、std::unique_ptr、std::weak_ptr。对于每种指针类型,都有一个非常量和一个 const 类型定义:

    • RawPtr and ConstRawPtr
    • SharedPtr and ConstSharedPtr
    • UniquePtr and ConstUniquePtr
    • WeakPtr and ConstWeakPtr

            为了与 ROS 1 相似,typedef Ptr 和 ConstPtr 仍然存在,但已被弃用。与 ROS 1 相比,它们使用 std::shared_ptr 而不是 Boost。

    五、服务(Services)

    对于服务,会生成一个名称相同且后跟下划线的结构。

    该结构仅包含两个 typedef:

    • 请求,这是服务请求部分的类型
    • 响应是服务请求部分的类型

    生成的代码与消息一样被拆分到多个文件中。

    5.1 请求和响应的消息(Request and response messages)

            对于这些服务的请求和响应,正在生成单独的消息以服务命名部分,并声明。_Request 或_Response 后缀。它们仍然在 srv 子命名空间中定义。

    六、结论

            ROS2的C++编程,目前规范其实在发展中,不太完善,但是做为开发人员需要紧跟当前的形势,了解其发展现状。

  • 相关阅读:
    一文掌握 Java8 Stream 中 Collectors 的 24 个操作
    bootstrap学习:自定义栅格系统
    计算机网络(自顶向下方法)-网络层
    31.springboot中的注解总结(spring,springmvc,springboot,mybatis,dubbo)
    搭建mysql8集群——一主一从
    win10环境安装kettle&linux环境安装kettle
    LeetCode-77-组合-回溯算法
    CT 扫描的 3D 图像分类-预测肺炎的存在
    基于tcp协议及数据库sqlite3的云词典项目
    Unity地面交互效果——1、局部UV采样和混合轨迹
  • 原文地址:https://blog.csdn.net/gongdiwudu/article/details/126258952