ros2 / ros2/rclcpp

Allow use of C message types

Open
#2,229 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

backlog
Dominant language
C++
Stars
805
Forks
564
Avg merge
1d 17h
Merged PRs (30d)
27

Description

Feature request

Feature description

Some interfaces with rcl internals, or other C-based implementations, are more easily accomplished by sticking with C message types - given that there is no universal conversion from C++-generated types to C-generated types, they are totally separate.

In one example case, rcl implements a service and provides a callback for it, but it is desirable to let rclcpp handle the executor/waitsets to take advantage of its threading/execution infrastructure. In that case, the RCL callback handler needs C message types to operate on.

I'll show the following example for Services, but there are similar difficulties using Publishers, etc that we could cover as part of this investigation.

The following

#include "std_srvs/srv/empty.h"
// ...
rclcpp::Service<std_srvs__srv__Empty> service;

fail to compile, for a couple of reasons:

  1. rosidl_generator_c does not generate a toplevel entity std_srvs__srv__Empty, instead just the _Request and _Response structs - as well as a typesupport getter function for the Service type
  2. Service looks for members as ::Request, etc.
Implementation considerations

Workaround example I am using for a C-based service (some error checking omitted for brevity)

// Helper wrapper for rclcpp::Service to access ::Request and ::Response types for allocation.
struct GetTypeDescriptionC
{
  using Request = type_description_interfaces__srv__GetTypeDescription_Request;
  using Response = type_description_interfaces__srv__GetTypeDescription_Response;
  using Event = type_description_interfaces__srv__GetTypeDescription_Event;
};
}  // namespace

// Helper function for C typesupport.
namespace rosidl_typesupport_cpp
{
template<>
rosidl_service_type_support_t const *
get_service_type_support_handle<GetTypeDescriptionC>()
{
  return ROSIDL_GET_SRV_TYPE_SUPPORT(type_description_interfaces, srv, GetTypeDescription);
}
}  // namespace rosidl_typesupport_cpp

void business(std::shared_ptr<rclcpp::Node> node) 
{
      auto rcl_node = node->get_node_base_interface()->get_rcl_node_handle();
      rcl_node_type_description_service_init(rcl_node);
    
      rcl_service_t * rcl_srv = nullptr;
      rcl_node_get_type_description_service(rcl_node, &rcl_srv);

      rclcpp::AnyServiceCallback<GetTypeDescriptionC> cb;
      cb.set(
        [node](
          std::shared_ptr<rmw_request_id_t> header,
          std::shared_ptr<GetTypeDescriptionC::Request> request,
          std::shared_ptr<GetTypeDescriptionC::Response> response
        ) {
          rcl_node_type_description_service_handle_request(
            node->get_node_base_interface()->get_rcl_node_handle(),
            header.get(),
            request.get(),
            response.get());
        });

      auto type_description_srv_ = std::make_shared<Service<GetTypeDescriptionC>>(
        node->get_node_base_interface()->get_shared_rcl_node_handle(),
        rcl_srv,
        cb);
      node->get_node_services_interface()->add_service(
        std::dynamic_pointer_cast<ServiceBase>(type_description_srv_),
        nullptr);
}

Maybe we want to call that "acceptable overhead" to create a C-based service? But I personally think that there could be a better way.

A followup note on this, there is no way to let rcl do its own take, because every code path in the default Executor does a take_request before ever invoking the user callback.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing rclcpp::Service, AnyServiceCallback, and the default Executor paths that call take_request before callbacks. Compare the C service Request, Response, and Event types with the ::Request and ::Response expectations described in the issue. Done means C-generated message types can be used through the relevant service and publisher APIs without the helper wrapper workaround, with the related execution paths covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.