〖ROS2 源码解析〗rclcpp/include/rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp

深入解析 ROS 2 rclcpp 的 NodeInterfaces 辅助工具

在 ROS 2 的rclcpp库中,rclcpp::node_interfaces::detail::NodeInterfacesHelpers.hpp文件提供了一系列模板类和辅助函数,用于支持NodeInterfaces类的实现。这些工具帮助管理和获取不同类型的节点接口,确保接口的正确初始化和使用。

一、文件概述

这个头文件主要包含了用于支持NodeInterfaces类的模板类和辅助函数。它定义了一些通用的模板结构,用于存储节点接口,并提供了构造函数和获取接口的方法。此外,还定义了一些宏,用于简化特定节点接口的支持添加过程。

二、主要模板类和结构

0. 源码

// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
#define RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_

#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>

#include "rclcpp/visibility_control.hpp"

namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{

// Support and Helper template classes for the NodeInterfaces class.

template<typename NodeT, typename ... Ts>
std::tuple<std::shared_ptr<Ts>...>
init_tuple(NodeT & n);

/// Stores the interfaces in a tuple, provides constructors, and getters.
template<typename ... InterfaceTs>
struct NodeInterfacesStorage
{
  template<typename NodeT>
  NodeInterfacesStorage(NodeT & node)  // NOLINT(runtime/explicit)
  : interfaces_(init_tuple<decltype(node), InterfaceTs ...>(node))
  {}

  NodeInterfacesStorage()
  : interfaces_()
  {}

  explicit NodeInterfacesStorage(std::shared_ptr<InterfaceTs>... args)
  : interfaces_(args ...)
  {}

  /// Individual Node Interface non-const getter.
  template<typename NodeInterfaceT>
  std::shared_ptr<NodeInterfaceT>
  get()
  {
    static_assert(
      (std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
      "NodeInterfaces class does not contain given NodeInterfaceT");
    return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
  }

  /// Individual Node Interface const getter.
  template<typename NodeInterfaceT>
  std::shared_ptr<const NodeInterfaceT>
  get() const
  {
    static_assert(
      (std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
      "NodeInterfaces class does not contain given NodeInterfaceT");
    return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
  }

protected:
  std::tuple<std::shared_ptr<InterfaceTs>...> interfaces_;
};

/// Prototype of NodeInterfacesSupports.
/**
 * Should read NodeInterfacesSupports<..., T, ...> as "NodeInterfaces supports T", and
 * if NodeInterfacesSupport is specialized for T, the is_supported should be
 * set to std::true_type, but by default it is std::false_type, which will
 * lead to a compiler error when trying to use T with NodeInterfaces.
 */
template<typename StorageClassT, typename ... Ts>
struct NodeInterfacesSupports;

/// Prototype of NodeInterfacesSupportCheck template meta-function.
/**
 * This meta-function checks that all the types given are supported,
 * throwing a more human-readable error if an unsupported type is used.
 */
template<typename StorageClassT, typename ... InterfaceTs>
struct NodeInterfacesSupportCheck;

/// Iterating specialization that ensures classes are supported and inherited.
template<typename StorageClassT, typename NextInterfaceT, typename ... RemainingInterfaceTs>
struct NodeInterfacesSupportCheck<StorageClassT, NextInterfaceT, RemainingInterfaceTs ...>
  : public NodeInterfacesSupportCheck<StorageClassT, RemainingInterfaceTs ...>
{
  static_assert(
    NodeInterfacesSupports<StorageClassT, NextInterfaceT>::is_supported::value,
    "given NodeInterfaceT is not supported by rclcpp::node_interfaces::NodeInterfaces");
};

/// Terminating case when there are no more "RemainingInterfaceTs".
template<typename StorageClassT>
struct NodeInterfacesSupportCheck<StorageClassT>
{};

/// Default specialization, needs to be specialized for each supported interface.
template<typename StorageClassT, typename ... RemainingInterfaceTs>
struct NodeInterfacesSupports
{
  // Specializations need to set this to std::true_type in addition to other interfaces.
  using is_supported = std::false_type;
};

/// Terminating specialization of NodeInterfacesSupports.
template<typename StorageClassT>
struct NodeInterfacesSupports<StorageClassT>
  : public StorageClassT
{
  /// Perfect forwarding constructor to get arguments down to StorageClassT.
  template<typename ... ArgsT>
  explicit NodeInterfacesSupports(ArgsT && ... args)
  : StorageClassT(std::forward<ArgsT>(args) ...)
  {}
};

// Helper functions to initialize the tuple in NodeInterfaces.

template<typename StorageClassT, typename ElementT, typename TupleT, typename NodeT>
void
init_element(TupleT & t, NodeT & n)
{
  std::get<std::shared_ptr<ElementT>>(t) =
    NodeInterfacesSupports<StorageClassT, ElementT>::get_from_node_like(n);
}

template<typename NodeT, typename ... Ts>
std::tuple<std::shared_ptr<Ts>...>
init_tuple(NodeT & n)
{
  using StorageClassT = NodeInterfacesStorage<Ts ...>;
  std::tuple<std::shared_ptr<Ts>...> t;
  (init_element<StorageClassT, Ts>(t, n), ...);
  return t;
}

/// Macro for creating specializations with less boilerplate.
/**
 * You can use this macro to add support for your interface class if:
 *
 * - The standard getter is get_node_{NodeInterfaceName}_interface(), and
 * - the getter returns a non-const shared_ptr<{NodeInterfaceType}>
 *
 * Examples of using this can be seen in the standard node interface headers
 * in rclcpp, e.g. rclcpp/node_interfaces/node_base_interface.hpp has:
 *
 *   RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)
 *
 * If your interface has a non-standard getter, or you want to instrument it or
 * something like that, then you'll need to create your own specialization of
 * the NodeInterfacesSupports struct without this macro.
 */
// *INDENT-OFF*
#define RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(NodeInterfaceType, NodeInterfaceName) \
  namespace rclcpp::node_interfaces::detail { \
  template<typename StorageClassT, typename ... RemainingInterfaceTs> \
  struct NodeInterfacesSupports< \
    StorageClassT, \
    NodeInterfaceType, \
    RemainingInterfaceTs ...> \
    : public NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...> \
  { \
    using is_supported = std::true_type; \
 \
    template<typename NodeT> \
    static \
    std::shared_ptr<NodeInterfaceType> \
    get_from_node_like(NodeT & node_like) \
    { \
      return node_like.get_node_ ## NodeInterfaceName ## _interface(); \
    } \
 \
    /* Perfect forwarding constructor to get arguments down to StorageClassT (eventually). */ \
    template<typename ... ArgsT> \
    explicit NodeInterfacesSupports(ArgsT && ... args) \
    : NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...>( \
        std::forward<ArgsT>(args) ...) \
    {} \
 \
    std::shared_ptr<NodeInterfaceType> \
    get_node_ ## NodeInterfaceName ## _interface() \
    { \
      return StorageClassT::template get<NodeInterfaceType>(); \
    } \
  }; \
  }  // namespace rclcpp::node_interfaces::detail
// *INDENT-ON*

}  // namespace detail
}  // namespace node_interfaces
}  // namespace rclcpp

#endif  // RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_

1. NodeInterfacesStorage

  • 用途:用于存储不同类型的节点接口指针,并提供构造函数和获取接口的方法。
  • 模板参数InterfaceTs...表示不同的节点接口类型。
  • 成员变量
    • interfaces_:存储节点接口指针的元组。
  • 构造函数
    • NodeInterfacesStorage(NodeT & node):接受一个节点对象作为参数,通过init_tuple函数初始化接口元组。
    • NodeInterfacesStorage():默认构造函数,创建一个空的接口元组。
    • NodeInterfacesStorage(std::shared_ptr<InterfaceTs>... args):接受多个接口指针作为参数,直接初始化接口元组。
  • 成员函数
    • template<typename NodeInterfaceT> std::shared_ptr<NodeInterfaceT> get():获取特定类型的节点接口指针。如果接口类型不匹配,会触发静态断言错误。
    • template<typename NodeInterfaceT> std::shared_ptr<const NodeInterfaceT> get() const:获取特定类型的常量节点接口指针。同样,如果接口类型不匹配,会触发静态断言错误。

2. NodeInterfacesSupports

  • 用途:用于检查特定类型的节点接口是否被支持,并提供获取接口的方法。这个模板类需要针对每个支持的接口进行特化。
  • 模板参数
    • StorageClassT:存储接口的类。
    • Ts...:表示不同的节点接口类型。
  • 成员变量
    • is_supported:静态成员类型,用于表示特定接口是否被支持。默认情况下为std::false_type,需要在特化中设置为std::true_type
  • 成员函数
    • template<typename NodeT> static std::shared_ptr<NodeInterfaceType> get_from_node_like(NodeT & node_like):静态方法,用于从节点对象中获取特定类型的接口指针。具体实现需要在特化中提供。
    • 构造函数:根据不同的特化情况,可以有不同的构造函数,用于将参数传递给基类构造函数。
    • std::shared_ptr<NodeInterfaceType> get_node_NodeInterfaceName_interface():获取特定类型的接口指针,内部调用StorageClassT::template get<NodeInterfaceType>()

3. NodeInterfacesSupportCheck

  • 用途:用于检查一组节点接口是否都被支持。通过递归特化,确保每个接口都被正确支持,并在编译时检测不支持的接口类型。
  • 模板参数
    • StorageClassT:存储接口的类。
    • InterfaceTs...:表示不同的节点接口类型。
  • 成员变量:无
  • 成员函数:无
  • 实现方式:通过递归特化,首先检查第一个接口是否被支持,如果支持,则继续检查剩余的接口。如果有任何一个接口不被支持,会触发静态断言错误。

三、辅助函数

1. init_element

  • 用途:用于初始化NodeInterfacesStorage中的特定接口元素。
  • 模板参数
    • StorageClassT:存储接口的类。
    • ElementT:要初始化的接口类型。
    • TupleT:接口元组类型。
    • NodeT:节点类型。
  • 实现方式:通过调用NodeInterfacesSupports<StorageClassT, ElementT>::get_from_node_like获取接口指针,并将其存储在元组中。

2. init_tuple

  • 用途:用于初始化NodeInterfacesStorage的接口元组。
  • 模板参数NodeT表示节点类型,Ts...表示不同的节点接口类型。
  • 实现方式
    • 使用using StorageClassT = NodeInterfacesStorage<Ts...>定义存储接口的类。
    • 创建一个接口元组t
    • 通过展开参数包调用init_element函数,初始化元组中的每个接口元素。
    • 返回初始化后的接口元组。

四、宏定义

RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT

  • 用途:用于简化特定节点接口的支持添加过程。如果接口的标准获取器是get_node_{NodeInterfaceName}_interface(),并且返回一个非常量的共享指针<{NodeInterfaceType}>,可以使用这个宏来添加对该接口的支持。
  • 参数
    • NodeInterfaceType:要支持的节点接口类型。
    • NodeInterfaceName:节点接口的名称,用于生成获取器的名称。
  • 实现方式
    • rclcpp::node_interfaces::detail命名空间中定义一个特化的NodeInterfacesSupports结构。
    • 设置is_supportedstd::true_type,表示该接口被支持。
    • 提供get_from_node_like静态方法,用于从节点对象中获取特定类型的接口指针,通过调用节点对象的get_node_NodeInterfaceName_interface()方法实现。
    • 提供构造函数,用于将参数传递给基类构造函数。
    • 提供get_node_NodeInterfaceName_interface()方法,用于获取特定类型的接口指针,内部调用StorageClassT::template get<NodeInterfaceType>()

五、总结

rclcpp::node_interfaces::detail::NodeInterfacesHelpers.hpp文件提供了一系列工具,用于支持NodeInterfaces类的实现。通过这些模板类和辅助函数,可以方便地管理和获取不同类型的节点接口,确保接口的正确初始化和使用。同时,宏定义的使用可以简化特定接口的支持添加过程,提高代码的可读性和可维护性。理解这些工具的实现对于深入理解rclcpp库中的节点接口管理机制非常重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值