layout | title | permalink | abstract | published | author | date_written | last_modified | categories |
---|---|---|---|---|---|---|---|---|
default |
Generated C++ interfaces |
articles/generated_interfaces_cpp.html |
This article describes the generated C++ code for ROS 2 interfaces. |
true |
[Dirk Thomas](https://github.com/dirk-thomas) |
2015-06 |
2019-03 |
Interfaces |
{:toc}
Authors: {{ page.author }}
Date Written: {{ page.date_written }}
Last Modified: {% if page.last_modified %}{{ page.last_modified }}{% else %}{{ page.date_written }}{% endif %}
This article specifies the generated C++ code for ROS interface types defined in the interface definition article.
All code of a ROS package should be defined in a namespace named after the package. To separate the generated code from other code within the package it is defined in a sub namespace:
- namespace for ROS messages:
<package_name>::msg
. - namespace for ROS services:
<package_name>::srv
.
ros1_bridge
.
Following the C++ style guide of ROS 2 the namespace hierarchy is mapped to a folder structure.
The filenames use lowercase alphanumeric characters with underscores for separating words and end with either .hpp
or .cpp
.
For a message a templated struct
with the same name followed by an underscore is generated.
The single template argument is the allocator for the data structure.
For ease of use there is a typedef
with the same name as the message which uses a default allocator (e.g. std::allocator
).
For each message two files are being generated:
<my_message_name>.hpp
currently only includes<my_message_name>__struct.hpp
<my_message_name>__struct.hpp
containing the definition of the struct
This allows to add additional files besides the one with the suffix __struct
to provide additional functionality.
For each additional functionality it can be decided to include it from the first header file.
<my_message_name>__traits.hpp
file
ROS type | C++ type |
---|---|
bool | bool |
byte | uint8_t |
char | char |
float32 | float |
float64 | double |
int8 | int8_t |
uint8 | uint8_t |
int16 | int16 |
uint16 | uint16 |
int32 | int32 |
uint32 | uint32 |
int64 | int64 |
uint64 | uint64_t |
string | std::string |
ROS type | C++ type |
---|---|
static array | std::array<T, N> |
unbounded dynamic array | std::vector |
bounded dynamic array | custom_class<T, N> |
bounded string | std::string |
The struct has same-named public member variables for every field of the message.
For each field a typedef
is created which is named after the member with a leading underscore and a trailing _type
.
Numeric constants are defined as enums
within the struct.
All other constants are declared as static const
members in the struct and their values are defined outside of the struct.
In the following discussion, "member" refers to the class member in the C++ class while "field" refers to the field definition in the IDL file.
The default constructor initializes all members with the default value specified in the IDL file, or otherwise with the common default for the field type as defined in this article (note: char
fields are considered numeric for C++).
In some cases this may not be desirable, since these fields will often be immediately overwritten with user-provided values.
Therefore, the constructor takes an optional directive of type rosidl_generator_cpp::MessageInitialization
to control how initialization is done:
MessageInitialization::ALL
- Initialize each member with the field's default value specified in the IDL file, or otherwise with the common default for the field type as defined in this article (note:char
fields are considered numeric for C++).- The safest option, and also the default (used if not passing any argument to the constructor).
MessageInitialization::SKIP
- Don't initialize any members; it is the user's responsibility to ensure that all members get initialized with some value, otherwise undefined behavior may result- Used for maximum performance if the user is setting all of the members themselves.
MessageInitialization::ZERO
- Zero initialize all members; all members will be value-initialized (dynamic size or upper boundary arrays will have zero elements), and default values from the message definition will be ignored- Used when the user doesn't want the overhead of initializing potentially complex or large default values, but still wants to ensure that all variables are properly initialized.
MessageInitialization::DEFAULTS_ONLY
- Initialize only members that have field default values; all other members will be left uninitialized- Minimal initialization which ensures that existing code has correctly initialized members when a new field with a default value is added to the IDL later.
Optionally the constructor can be invoked with an allocator.
The struct has no constructor with positional arguments for the members. The short reason for this is that if code would rely on positional arguments to construct data objects changing a message definition would break existing code in subtle ways. Since this would discourage evolution of message definitions the data structures should be populated by setting the members separately, e.g. using the setter methods.
For each field a setter method is generated to enable method chaining.
They are named after the fields with a leading set__
.
The setter methods have a single argument to pass the value for the member variable.
Each setter method returns the struct itself.
The comparison operators ==
and !=
perform the comparison on a per member basis.
The struct contains typedefs
for the four common pointer types plain pointer
, std::shared_ptr
, std::unique_ptr
, std::weak_ptr
.
For each pointer type there a non-const and a const typedef
:
RawPtr
andConstRawPtr
SharedPtr
andConstSharedPtr
UniquePtr
andConstUniquePtr
WeakPtr
andConstWeakPtr
For similarity to ROS 1 the typedefs
Ptr
and ConstPtr
still exist but are deprecated.
In contrast to ROS 1 they use std::shared_ptr
instead of Boost.
For a service a struct
with the same name followed by an underscore is generated.
The struct contains only two typedefs
:
Request
which is the type of the request part of the serviceResponse
which is the type of the request part of the service
The generated code is split across multiple files the same way as message are.
For the request and response parts of a service separate messages are being generated.
These messages are named after the service and have either a _Request
or _Response
suffix.
They are are still defined in the srv
sub namespace.