Skip to content

File generic_subscription.hpp

File List > agnocast_wrapper > generic_subscription.hpp

Go to the documentation of this file

// Copyright 2025 TIER IV, 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.

#pragma once

// Type-erased ("generic") subscription abstraction for the Agnocast build.

#include <rclcpp/qos_overriding_options.hpp>
#include <rclcpp/serialized_message.hpp>

#include <functional>
#include <memory>
#include <stdexcept>
#include <string>

namespace nebula::agnocast_wrapper
{

using GenericSubscriptionCallback =
  std::function<void(std::shared_ptr<const rclcpp::SerializedMessage>)>;

namespace detail
{

inline void check_generic_subscription_qos_overriding_options(
  const rclcpp::QosOverridingOptions & qos_overriding_options, const std::string & topic_name)
{
  if (!qos_overriding_options.get_policy_kinds().empty()) {
    throw std::invalid_argument(
      "create_generic_subscription(" + topic_name +
      "): qos_overriding_options is not supported for a generic subscription (honored by "
      "Agnocast's GenericSubscription but silently ignored by rclcpp's, so it cannot behave "
      "consistently across backends or builds)");
  }
}

}  // namespace detail

}  // namespace nebula::agnocast_wrapper

#ifdef USE_AGNOCAST_ENABLED

#include "nebula_core_ros/agnocast_wrapper/runtime.hpp"
#include "nebula_core_ros/agnocast_wrapper/subscription.hpp"

#include <agnocast/agnocast.hpp>
#include <rclcpp/rclcpp.hpp>

#include <cstddef>
#include <utility>

namespace nebula::agnocast_wrapper
{

class GenericSubscription
{
public:
  using SharedPtr = std::shared_ptr<GenericSubscription>;

  virtual ~GenericSubscription() = default;

  virtual const char * get_topic_name() const = 0;

  virtual rclcpp::QoS get_actual_qos() const = 0;
};

class AgnocastGenericSubscription : public GenericSubscription
{
  agnocast::GenericSubscription::SharedPtr subscription_;

public:
  template <typename NodeT>
  explicit AgnocastGenericSubscription(
    NodeT * node, const std::string & topic_name, const std::string & topic_type,
    const rclcpp::QoS & qos, GenericSubscriptionCallback callback,
    const agnocast::SubscriptionOptions & options)
  {
    detail::check_generic_subscription_qos_overriding_options(
      options.qos_overriding_options, topic_name);
    subscription_ = agnocast::create_generic_subscription(
      node, topic_name, topic_type, qos, std::move(callback), options);
  }

  const char * get_topic_name() const override { return subscription_->get_topic_name(); }
  rclcpp::QoS get_actual_qos() const override { return subscription_->get_actual_qos(); }
};

class ROS2GenericSubscription : public GenericSubscription
{
  rclcpp::GenericSubscription::SharedPtr subscription_;

public:
  explicit ROS2GenericSubscription(
    rclcpp::Node * node, const std::string & topic_name, const std::string & topic_type,
    const rclcpp::QoS & qos, GenericSubscriptionCallback callback,
    const agnocast::SubscriptionOptions & options)
  {
    detail::check_generic_subscription_qos_overriding_options(
      options.qos_overriding_options, topic_name);
    subscription_ = node->create_generic_subscription(
      topic_name, topic_type, qos, std::move(callback), to_rclcpp_subscription_options(options));
  }

  const char * get_topic_name() const override { return subscription_->get_topic_name(); }
  rclcpp::QoS get_actual_qos() const override { return subscription_->get_actual_qos(); }
};

inline GenericSubscription::SharedPtr create_generic_subscription(
  rclcpp::Node * node, const std::string & topic_name, const std::string & topic_type,
  const rclcpp::QoS & qos, GenericSubscriptionCallback callback,
  const agnocast::SubscriptionOptions & options = agnocast::SubscriptionOptions{})
{
  if (use_agnocast()) {
    return std::make_shared<AgnocastGenericSubscription>(
      node, topic_name, topic_type, qos, std::move(callback), options);
  } else {
    return std::make_shared<ROS2GenericSubscription>(
      node, topic_name, topic_type, qos, std::move(callback), options);
  }
}

inline GenericSubscription::SharedPtr create_generic_subscription(
  rclcpp::Node * node, const std::string & topic_name, const std::string & topic_type,
  const size_t qos_history_depth, GenericSubscriptionCallback callback,
  const agnocast::SubscriptionOptions & options = agnocast::SubscriptionOptions{})
{
  return create_generic_subscription(
    node, topic_name, topic_type, rclcpp::QoS(rclcpp::KeepLast(qos_history_depth)),
    std::move(callback), options);
}

}  // namespace nebula::agnocast_wrapper

#else

#include <rclcpp/rclcpp.hpp>

#include <cstddef>
#include <utility>

namespace nebula::agnocast_wrapper
{

inline rclcpp::GenericSubscription::SharedPtr create_generic_subscription(
  rclcpp::Node * node, const std::string & topic_name, const std::string & topic_type,
  const rclcpp::QoS & qos, GenericSubscriptionCallback callback,
  const rclcpp::SubscriptionOptions & options = rclcpp::SubscriptionOptions{})
{
  detail::check_generic_subscription_qos_overriding_options(
    options.qos_overriding_options, topic_name);
  return node->create_generic_subscription(
    topic_name, topic_type, qos, std::move(callback), options);
}

inline rclcpp::GenericSubscription::SharedPtr create_generic_subscription(
  rclcpp::Node * node, const std::string & topic_name, const std::string & topic_type,
  const size_t qos_history_depth, GenericSubscriptionCallback callback)
{
  return create_generic_subscription(
    node, topic_name, topic_type, rclcpp::QoS(rclcpp::KeepLast(qos_history_depth)),
    std::move(callback));
}

}  // namespace nebula::agnocast_wrapper

#endif