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:
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
- 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.
Feature request
Feature description
Some interfaces with
rclinternals, 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,
rclimplements a service and provides a callback for it, but it is desirable to letrclcpphandle 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
fail to compile, for a couple of reasons:
rosidl_generator_cdoes not generate a toplevel entitystd_srvs__srv__Empty, instead just the_Requestand_Responsestructs - as well as a typesupport getter function for the Service type::Request, etc.Implementation considerations
Workaround example I am using for a C-based service (some error checking omitted for brevity)
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
rcldo its owntake, because every code path in the defaultExecutordoes atake_requestbefore ever invoking the user callback.