diff --git a/include/message_filters/subscriber.h b/include/message_filters/subscriber.h index 387758a..ac4e633 100644 --- a/include/message_filters/subscriber.h +++ b/include/message_filters/subscriber.h @@ -39,6 +39,8 @@ #include #include +#include +#include #include "message_filters/connection.h" #include "message_filters/simple_filter.h" @@ -46,67 +48,65 @@ namespace message_filters { -template +/// Utility struct for highlighting deprecated template parameters +struct DeprecatedTemplateParameter {}; + +template class SubscriberBase { public: - typedef std::shared_ptr NodePtr; + [[deprecated]] typedef std::shared_ptr NodePtr; + + using NodeParametersInterface = rclcpp::node_interfaces::NodeParametersInterface; + using NodeTopicsInterface = rclcpp::node_interfaces::NodeTopicsInterface; + + using RequiredInterfaces = rclcpp::node_interfaces::NodeInterfaces; + + template{}, int> = 0> + SubscriberBase() {} + + template{}, int> = 0> + [[deprecated("Template parameter of SubscriberBase class has been deprecated and will be removed in future releases")]] + SubscriberBase() {} virtual ~SubscriberBase() = default; + /** - * \brief Subscribe to a topic. - * - * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. - * - * \param node The rclcpp::Node::SharedPtr to use to subscribe. - * \param topic The topic to subscribe to. - * \param qos (optional) The rmw qos profile to use to subscribe + * \brief Subscribe to a topic (deprecated in favor of NodeInterfaces interface). */ - virtual void subscribe(NodePtr node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) = 0; + [[deprecated]] virtual void subscribe(NodePtr /*node*/, const std::string& /*topic*/, + const rmw_qos_profile_t /*qos*/ = rmw_qos_profile_default) {}; /** - * \brief Subscribe to a topic. - * - * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. - * - * \param node The rclcpp::Node to use to subscribe. - * \param topic The topic to subscribe to. - * \param qos (optional) The rmw qos profile to use to subscribe + * \brief Subscribe to a topic (deprecated in favor of NodeInterfaces interface). */ - virtual void subscribe(NodeType * node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) = 0; + [[deprecated]] virtual void subscribe(NodeType * /*node*/, const std::string& /*topic*/, + const rmw_qos_profile_t /*qos*/ = rmw_qos_profile_default) {}; /** * \brief Subscribe to a topic. * * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. - * This override allows SubscriptionOptions to be passed into the class without changing API. * - * \param node The rclcpp::Node::SharedPtr to use to subscribe. + * \param node The NodeInterfaces to use to subscribe. * \param topic The topic to subscribe to. - * \param qos (optional) The rmw qos profile to use to subscribe. - * \param options The subscription options to use to subscribe. + * \param qos (optional) The rmw qos profile to use to subscribe */ - virtual void subscribe( - NodePtr node, - const std::string& topic, - const rmw_qos_profile_t qos, - rclcpp::SubscriptionOptions options) - { - this->subscribe(node.get(), topic, qos, options); - }; + virtual void subscribe(RequiredInterfaces node_interfaces, const std::string& topic, + const rmw_qos_profile_t qos = rmw_qos_profile_default) = 0; /** * \brief Subscribe to a topic. * * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. * - * \param node The rclcpp::Node to use to subscribe. + * \param node The NodeInterfaces to use to subscribe. * \param topic The topic to subscribe to. * \param qos The rmw qos profile to use to subscribe. * \param options The subscription options to use to subscribe. */ virtual void subscribe( - NodeType * node, + RequiredInterfaces node_interfaces, const std::string& topic, const rmw_qos_profile_t qos, rclcpp::SubscriptionOptions options) = 0; @@ -120,6 +120,7 @@ class SubscriberBase */ virtual void unsubscribe() = 0; }; + template using SubscriberBasePtr = std::shared_ptr>; @@ -161,62 +162,124 @@ struct message_type template using message_type_t = typename message_type::type; -template -class Subscriber +template +class Subscriber : public SubscriberBase , public SimpleFilter> { public: - typedef std::shared_ptr NodePtr; typedef message_type_t MessageType; typedef MessageEvent EventType; + // Note: can be removed once the deprecated template parameter NodeType is removed + using RequiredInterfaces = typename SubscriberBase::RequiredInterfaces; + using NodeParametersInterface = typename SubscriberBase::NodeParametersInterface; + using NodeTopicsInterface = typename SubscriberBase::NodeTopicsInterface; + /** - * \brief Constructor - * - * See the rclcpp::Node::subscribe() variants for more information on the parameters + * \brief Solely for highlighting deprecated template parameters with a compiler warning + */ + template{}, int> = 0> + [[deprecated("Second template parameter of Subscriber class is deprecated and will be removed in a future release")]] + Subscriber(RequiredInterfaces node_interfaces, + const std::string& topic, + const rmw_qos_profile_t qos, + rclcpp::SubscriptionOptions options) + { + subscribe(node_interfaces, topic, qos, options); + } + + /** + * \brief Solely for highlighting deprecated template parameters with a compiler warning + */ + template{}, int> = 0> + [[deprecated("Second template parameter of Subscriber class is deprecated and will be removed in a future release")]] + Subscriber(RequiredInterfaces node_interfaces, const std::string& topic, + const rmw_qos_profile_t qos = rmw_qos_profile_default) + { + subscribe(node_interfaces, topic, qos); + } + + /** + * \brief Constructor for rclcpp::Node / rclcpp_lifecycle::LifecycleNode. * - * \param node The rclcpp::Node::SharedPtr to use to subscribe. + * \param node The NodeInterfaces to use to subscribe. * \param topic The topic to subscribe to. * \param qos (optional) The rmw qos profile to use to subscribe */ - Subscriber(NodePtr node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) + template{}, int> = 0> + Subscriber(RequiredInterfaces node_interfaces, const std::string& topic, + const rmw_qos_profile_t qos = rmw_qos_profile_default) { - subscribe(node, topic, qos); + subscribe(node_interfaces, topic, qos); } - Subscriber(NodeType * node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) - { - subscribe(node, topic, qos); - } + /** + * \brief Constructor for rclcpp::Node::SharedPtr / rclcpp_lifecycle::LifecycleNode::SharedPtr. + * + * \param node The NodeT::SharedPtr to use to subscribe. + * \param topic The topic to subscribe to. + * \param qos (optional) The rmw qos profile to use to subscribe + */ + template + Subscriber(std::shared_ptr node, const std::string& topic, + const rmw_qos_profile_t qos = rmw_qos_profile_default) : Subscriber(node.get(), topic, qos) {} /** - * \brief Constructor + * \brief Constructor for raw pointer to rclcpp::Node / rclcpp_lifecycle::LifecycleNode. * - * See the rclcpp::Node::subscribe() variants for more information on the parameters + * \param node The NodeT raw pointer to use to subscribe. + * \param topic The topic to subscribe to. + * \param qos (optional) The rmw qos profile to use to subscribe + */ + template + Subscriber(NodeT * node, const std::string& topic, + const rmw_qos_profile_t qos = rmw_qos_profile_default) : Subscriber(*node, topic, qos) {} + + /** + * \brief Constructor for rclcpp::Node / rclcpp_lifecycle::LifecycleNode. * - * \param node The rclcpp::Node::SharedPtr to use to subscribe. + * \param node_interfaces The NodeInterfaces to use to subscribe. * \param topic The topic to subscribe to. * \param qos The rmw qos profile to use to subscribe. * \param options The subscription options to use to subscribe. */ - Subscriber( - NodePtr node, - const std::string& topic, - const rmw_qos_profile_t qos, - rclcpp::SubscriptionOptions options) + template{}, int> = 0> + Subscriber(RequiredInterfaces node_interfaces, + const std::string& topic, + const rmw_qos_profile_t qos, + rclcpp::SubscriptionOptions options) { - subscribe(node.get(), topic, qos, options); + subscribe(node_interfaces, topic, qos, options); } - Subscriber( - NodeType * node, - const std::string& topic, - const rmw_qos_profile_t qos, - rclcpp::SubscriptionOptions options) - { - subscribe(node, topic, qos, options); - } + /** + * \brief Constructor for rclcpp::Node::SharedPtr / rclcpp_lifecycle::LifecycleNode::SharedPtr. + * + * \param node The NodeT::SharedPtr to use to subscribe. + * \param topic The topic to subscribe to. + * \param qos The rmw qos profile to use to subscribe. + * \param options The subscription options to use to subscribe. + */ + template + Subscriber(std::shared_ptr node, + const std::string& topic, + const rmw_qos_profile_t qos, + rclcpp::SubscriptionOptions options) : Subscriber(node.get(), topic, qos, options) {} + + /** + * \brief Constructor for raw pointer to rclcpp::Node / rclcpp_lifecycle::LifecycleNode. + * + * \param node The NodeT raw pointer to use to subscribe. + * \param topic The topic to subscribe to. + * \param qos The rmw qos profile to use to subscribe. + * \param options The subscription options to use to subscribe. + */ + template + Subscriber(NodeT * node, + const std::string& topic, + const rmw_qos_profile_t qos, + rclcpp::SubscriptionOptions options) : Subscriber(*node, topic, qos, options) {} /** * \brief Empty constructor, use subscribe() to subscribe to a topic @@ -233,13 +296,14 @@ class Subscriber * * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. * - * \param node The rclcpp::Node::SharedPtr to use to subscribe. + * \param node_interfaces The NodeInterfaces to use to subscribe. * \param topic The topic to subscribe to. * \param qos (optional) The rmw qos profile to use to subscribe */ - void subscribe(NodePtr node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) override + // TODO(wjwwood): deprecate in favor of API's that use `rclcpp::QoS` instead. + void subscribe(RequiredInterfaces node_interfaces, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) override { - subscribe(node.get(), topic, qos, rclcpp::SubscriptionOptions()); + subscribe(node_interfaces, topic, qos, rclcpp::SubscriptionOptions()); } /** @@ -247,14 +311,15 @@ class Subscriber * * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. * - * \param node The rclcpp::Node to use to subscribe. + * \param node The NodeT::SharedPtr to use to subscribe. * \param topic The topic to subscribe to. * \param qos (optional) The rmw qos profile to use to subscribe */ // TODO(wjwwood): deprecate in favor of API's that use `rclcpp::QoS` instead. - void subscribe(NodeType * node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) override + template + void subscribe(std::shared_ptr node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) { - subscribe(node, topic, qos, rclcpp::SubscriptionOptions()); + subscribe(node.get(), topic, qos, rclcpp::SubscriptionOptions()); } /** @@ -262,20 +327,15 @@ class Subscriber * * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. * - * \param node The rclcpp::Node::SharedPtr to use to subscribe. + * \param node The NodeT raw pointer to use to subscribe. * \param topic The topic to subscribe to. - * \param qos The rmw qos profile to use to subscribe. - * \param options The subscription options to use to subscribe. + * \param qos (optional) The rmw qos profile to use to subscribe */ - void subscribe( - NodePtr node, - const std::string& topic, - const rmw_qos_profile_t qos, - rclcpp::SubscriptionOptions options) override + // TODO(wjwwood): deprecate in favor of API's that use `rclcpp::QoS` instead. + template + void subscribe(NodeT * node, const std::string& topic, const rmw_qos_profile_t qos = rmw_qos_profile_default) { - subscribe(node.get(), topic, qos, options); - node_raw_ = nullptr; - node_shared_ = node; + subscribe(*node, topic, qos, rclcpp::SubscriptionOptions()); } /** @@ -283,14 +343,14 @@ class Subscriber * * If this Subscriber is already subscribed to a topic, this function will first unsubscribe. * - * \param node The rclcpp::Node to use to subscribe. + * \param node The NodeInterfaces to use to subscribe. * \param topic The topic to subscribe to. * \param qos The rmw qos profile to use to subscribe * \param options The subscription options to use to subscribe. */ // TODO(wjwwood): deprecate in favor of API's that use `rclcpp::QoS` instead. void subscribe( - NodeType * node, + RequiredInterfaces node_interfaces, const std::string& topic, const rmw_qos_profile_t qos, rclcpp::SubscriptionOptions options) override @@ -304,12 +364,21 @@ class Subscriber rclcpp_qos.get_rmw_qos_profile() = qos; qos_ = qos; options_ = options; - sub_ = node->template create_subscription(topic, rclcpp_qos, - [this](const std::shared_ptr msg) { - this->cb(EventType(msg)); - }, options); - node_raw_ = node; + // Note: temporary variables are solely needed because the involved submodules are inconsistent about the passing + // of shared_ptr. While the node_interfaces return by value, the create_subscription function expects the + // shared_ptr to be passed via reference... + auto parameters_interface = node_interfaces.template get(); + auto topics_interface = node_interfaces.template get(); + + sub_ = rclcpp::create_subscription(parameters_interface, + topics_interface, + topic, rclcpp_qos, + [this](const std::shared_ptr msg) { + this->cb(EventType(msg)); + }, options); + + node_interfaces_ = node_interfaces; } } @@ -320,11 +389,7 @@ class Subscriber { if (!topic_.empty()) { - if (node_raw_ != nullptr) { - subscribe(node_raw_, topic_, qos_, options_); - } else if (node_shared_ != nullptr) { - subscribe(node_shared_, topic_, qos_, options_); - } + subscribe(node_interfaces_, topic_, qos_, options_); } } @@ -372,8 +437,7 @@ class Subscriber typename rclcpp::Subscription::SharedPtr sub_; - NodePtr node_shared_; - NodeType * node_raw_ {nullptr}; + RequiredInterfaces node_interfaces_; std::string topic_; rmw_qos_profile_t qos_; diff --git a/test/test_subscriber.cpp b/test/test_subscriber.cpp index 88d5ab2..10339a2 100644 --- a/test/test_subscriber.cpp +++ b/test/test_subscriber.cpp @@ -287,6 +287,31 @@ TEST(Subscriber, lifecycle) ASSERT_GT(h.count_, 0); } +TEST(Subscriber, node_interfaces) +{ + auto node = std::make_shared("test_node"); + Helper h; + + // disassemble node into relevant interfaces + using NodeParametersInterface = rclcpp::node_interfaces::NodeParametersInterface; + using NodeTopicsInterface = rclcpp::node_interfaces::NodeTopicsInterface; + using RequiredInterfaces = rclcpp::node_interfaces::NodeInterfaces; + + message_filters::Subscriber sub(RequiredInterfaces(*node), "test_topic"); + sub.registerCallback(std::bind(&Helper::cb, &h, std::placeholders::_1)); + auto pub = node->create_publisher("test_topic", 10); + pub->on_activate(); + rclcpp::Clock ros_clock; + auto start = ros_clock.now(); + while (h.count_ == 0 && (ros_clock.now() - start) < rclcpp::Duration(1, 0)) { + pub->publish(Msg()); + rclcpp::Rate(50).sleep(); + rclcpp::spin_some(node->get_node_base_interface()); + } + + ASSERT_GT(h.count_, 0); +} + int main(int argc, char **argv) {