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.
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:
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.