ROS Resources: Documentation | Support | Discussion Forum | Service Status | Q&A answers.ros.org

Help to implement an other RMW


#1

This post is following a previous github issue.

Thank you for your answer, @wjwwood

I understand better. But I have an other question.

If for example I want to receive a message of type std_msgs::msg::String, does it implies that the parameter ros_message of rwm_take, which is a void* should be casted in std_msgs::msg::String ? (I tried so, and the c++ listener example worked)

Does it mean that if an example is written in an other language (C, for example), it should be casted into std_msgs__msg__String ? How do rmw manages that ?

I guess all this should be handled by “rosidl typesupport things”, but since it is generated, I have a hard time to understand what happens there.


#2

Yes. The type you need to pass to take and publish is determined by the rosidl_message_type_support_t structure you give when creating the publisher or subscription.

For messages in C, there is a macro ROSIDL_GET_MSG_TYPE_SUPPORT which when given a message type as arguments will resolve to a C function name:

In C++, the same thing is done, but using a template instead of a macro to get the specialized function name:

In either case the resulting function name/namespace encapsulates:

  • The typesupport system
  • The message package
  • The message name
  • The native language (C or C++)

This function will return a rosidl_message_type_support_t structure which is tailored to that combination.

The “typesupport system” is basically a way to select one of a few typesupport implementations. Some are specific to a particular DDS vendor, like rosidl_typesupport_connext_c for C
or rosidl_typesupport_connext_cpp for C++, but other implementations (like Fast-RTPS) use a general type support implementation called rosidl_typesupport_introspection_c
for C or rosidl_typesupport_introspection_cpp for C++.

These different implementations of typesupport essentially determine how serialization occurs, but can be responsible for other things as well.

The message package and name uniquely identify the message, so the same type support structure cannot be used for std_msgs::msg::String and std_msgs::msg::Int64. You need a unique one for each message type. This is typically ok, since publishers and subscriptions only work with one type at a time (at least for now).

Finally, the rosidl structure is unique to the native language for the message type. I use the term native language because a C client library and the Python client library might both use the C type support system. We have a specialized C++ type support system so we don’t have to use the C message structures directly in C++ and so that we don’t have to convert C++ message structures to C at any point. Practically it means that if you, for example, create a publisher of type std_msgs/String using the rosidl_message_type_support_t structure for C, then you can only publish std_msgs__msg__String with it and you cannot publish messages of the C++ type std_msgs::msg::String.

I know that seems complicated, but it’s the most straightforward way we have found to delegate serialization of custom types. DDS uses a very similar system for separating message type agnostic code from the message specific code.

Unfortunately the C code requires us to lose type safety when calling publish, for example. Nothing stops you from creating a publisher for one type but passing a different type into the void * argument of rcl_publish except that we say it’s undefined behavior.

In C++, however, we’re able to use templates and RTTI to enforce the types match, at least. We’re even able to enforce this at compile time in some cases.

Hopefully that gives you (and others that come across this) a basic understanding of the moving parts are and why they exist.

As I described above, if you want to use C++ message types, then you need to specify that when creating the publisher or subscription. If you have a C message type, but the publisher is C++, then you’d need to convert (i.e. copy not cast) the C data structure to a C++ one first.


#3

Thanks for your detailed answer !