This describes defining interfaces using a subset of the Interface Definition Language (IDL).
Authors: Dirk Thomas
Date Written: 2019-03
Last Modified: 2020-07
This article identifies a subset of the Interface Definition Language (IDL) that can be used to describe interfaces between components. Further it describes how the interfaces are used to generate code in C, C++ and Python.
ROS 2 supports a subset of the OMG IDL 4.2 specification. Whatever is not listed below is probably not supported at the moment (e.g. enums).
Both line comments (//
) as well as block comments (/* ... */
) are being supported.
An identifier must start with an is an ASCII alphabetic characteran followed by any number of ASCII alphabetic, digit and underscore (_
) characters.
All of the following literals are supported:
At the moment there is no preprocessing happening when reading the `.idl files.
Imports must be used to reference other .idl
file which declare data types
used in this .idl
file.
Each module must contain at least one definition and they can be nested.
For ROS interfaces the first module level commonly represents the package name,
the second module level distinguishes the type of the interface (msg
, srv
,
action
).
A structure must contain at least one member.
IDL type | Value range |
---|---|
short | -2^15 … 2^15 - 1 |
unsigned short | 0 … 2^16 - 1 |
long | -2^31 … 2^31 - 1 |
unsigned long | 0 … 2^32 - 1 |
long long | -2^63 … 2^63 - 1 |
unsigned long long | 0 … 2^64 - 1 |
IDL type | Format |
---|---|
float | IEEE single-precision floating point number |
double | IEEE double-precision floating point number |
long double | IEEE double-extended floating-point number |
IDL type | Value range |
---|---|
char | A 8-bit single-byte character with a numerical value between 0 and 255 (see 7.2.6.2.1) |
The type can store a single-byte character from any byte-oriented code set, or when used in an array, encode a multi-byte character from a multi-byte code set.
IDL type | Value range |
---|---|
wchar | A 16-bit wide character |
While the IDL spec only defines the size of wchar as implementation-dependent the DDS-XTypes specification 1.2 defines it with 16-bit.
IDL type | Value range |
---|---|
boolean | One of the values TRUE and FALSE |
IDL type | Value range |
---|---|
octet | opaque 8-bit |
IDL type | Value range |
---|---|
int8 | -2^7 … 2^7 - 1 |
uint8 | 0 … 2^8 - 1 |
IDL type | Equivalent IDL type |
---|---|
int16 | short |
uint16 | unsigned short |
int32 | long |
uint32 | unsigned long |
int64 | long long |
uint64 | unsigned long long |
IDL type | Value range |
---|---|
sequence<type_spec> | sequence of items of the specific type_spec |
the sequence is unbounded and no maximum size is specified | |
sequence<type_spec, N> | sequence of of up to N items of the specified type_spec |
the sequence is bounded and contain between 0 and N items |
IDL type | Value range |
---|---|
string | sequence of char except null |
IDL type | Value range |
---|---|
wstring | sequence of wchar except null |
A structure is a grouping of at least one member.
An enumerated type consist of an ordered list of enumerators.
A multidimensional, fixed-size array is defined by the type of each item and the explicit sizes for each dimension. For now only a one-dimensional, fixed-size array is supported though.
The syntax for arbitrary annotations is supported. How each annotation type is being handled depends on the code generation described below.
TODO: All of this too
Generated code for messages is only guaranteed to enforce constraints when a message is published. For example, there might not be an error when a field constrained to be a fixed size array is assigned an array with the wrong size. This could be inconsistent across client library implementations depending on language features and performance cost. In the future any client library may add additional constraint checking when a field is set or modified. Users of generated message code should assume constraints could be enforced at any time to be compatible with such a change.
The next table defines how IDL types are mapped to the following programming languages:
IDL type | C type | C++ type | Python type |
---|---|---|---|
float | float | float | float |
double | double | double | float |
long double | long double | long double2 | float |
char | unsigned char | unsigned char | str with length 1 |
wchar | char16_t | char16_t | str with length 1 |
boolean | _Bool | bool | bool |
octet | unsigned char | std::byte1 | bytes with length 1 |
int8 | int8_t | int8_t | int |
uint8 | uint8_t | uint8_t | int |
int16 | int16_t | int16_t | int |
uint16 | uint16_t | uint16_t | int |
int32 | int32_t | int32_t | int |
uint32 | uint32_t | uint32_t | int |
int64 | int64_t | int64_t | int |
uint64 | uint64_t | uint64_t | int |
long double
with their choice of middleware and platform.
For example, it is only 64 bits when using Visual Studio.The next table defines how IDL templated and constructed types are mapped to the programming languages (unless specified otherwise below).
When specified T
is either one of the types above or an IDL struct.
N
is the upper limit of a bounded type.
IDL type | C type | C++ type | Python type |
---|---|---|---|
T[N] | T[N] | std::array<T, N> | list |
sequence<T> | struct {size_t, T * } | std::vector<T> | list |
sequence<T, N> | struct {size_t, T * }, size_t N | std::vector<T> | list |
string | char * | std::string | str |
string<N> | char * | std::string | str |
wstring | char16_t * | std::u16string | str |
wstring<N> | char16_t * | std::u16string | str |
These array and sequence types have special mappings. If a cell is blank then the default mapping is used.
IDL type | C type | C++ type | Python type | ||
---|---|---|---|---|---|
T[N] | for numeric types T: float, double, int8, uint8, int16, uint16, int32, uint32, int64, uint64 |
- | - | numpy.ndarray( shape=(N, ), dtype=numpy.DT) |
where DT is determined by T: float -> float32, double -> float64, intX -> intX, uintX -> uintX |
sequence<T> sequence<T, N> |
for numeric types T: float, double, int8, uint8, int16, uint16, int32, uint32, int64, uint64 |
- | - | array.array(typecode=TC) | where TC is determined by T: float -> f, double -> d, int8 -> b, uint8 -> B, int16 -> h, uint16 -> H, int32 -> l, uint32 -> L, int64 -> q, uint64 -> Q |
octet[N] | - | - | bytes | ||
sequence<octet> | - | - | bytes | ||
sequence<octet, N> | - | - | bytes |
ROS 2 nodes exchange information of a certain real world object by means of topics. Every message in a topic is known as data sample and represents an update to the status of the object.
Topic instances are a way of multiplexing the transmission of updates of several objects of the same logical kind over the same resource, i.e. the topic. In a keyed topic, every message is associated with a topic instance and each topic instance is identified by a unique key. In this sense, keys can be thought as the primary key of a database. This key allows nodes to update different states of the same kind. In consequence, all data samples sharing the same key value refer to the same object.
The @key
annotation allows indicating that a data member is part of the key, which can have zero or more key fields and can be applied to structure fields of various types.
The following example shows how to define a keyed message using the IDL format:
# KeyedMsgName.idl
module package_name {
module msg {
struct KeyedMsgName {
@key long member1;
string member2;
};
};
};
Currently, the message interface format that supports the @key
annotation is .idl
.
Hence, .msg
and .srv
do not support the @key
annotation yet.
Please refer to Conversion to IDL for further information on the equivalence and conversion among them.
The general-purpose idl types including primitive types, sequences, strings and structs can be annotated as keys. Following are some examples:
Type | Key Members |
---|---|
struct NoKey { boolean member1; long member2; long member3; } |
None |
struct SimpleKey { @key long member1; long member2; } |
member1 |
struct ArrayKey { @key long member1[3]; } |
member1[0] member1[1] member1[2] |
struct StringKey { @key string member1; long member2} |
member1 |
struct NestedNoKey { SimpleKey member1; long member2; } |
None |
struct NestedKey { @key SimpleKey member1; long member2; } |
member1.member1 |
struct NestedKey2 { @key NoKey member1; long member2; } |
member1.member1 member1.member2 member1.member3 |
struct ComplexNestedKey { @key NestedNoKey member1; long member2; } |
member1.member1.member1 member1.member2 |
The default value is being used to initialize struct members.
When the value comment
is passed as the language
parameter the text parameter is used as the docblock for the annotated element across all supported programming languages.