From 98b0632556b5972d5c0f5cf0ae9d99e5bfc7cb56 Mon Sep 17 00:00:00 2001 From: Roman Sokolkov Date: Mon, 20 Jun 2022 13:23:15 +0200 Subject: [PATCH] Add enum support to ROSIDL Signed-off-by: Roman Sokolkov --- rosidl_generator_c/CMakeLists.txt | 1 + .../resource/msg__functions.c.em | 103 +++++++++- .../resource/msg__functions.h.em | 59 ++++++ rosidl_generator_c/resource/msg__struct.h.em | 27 +++ .../rosidl_generator_c/__init__.py | 14 ++ rosidl_generator_c/test/test_interfaces.c | 188 ++++++++++++++++++ rosidl_generator_cpp/CMakeLists.txt | 1 + .../resource/msg__struct.hpp.em | 10 + .../resource/msg__traits.hpp.em | 18 ++ .../rosidl_generator_cpp/__init__.py | 17 ++ rosidl_generator_cpp/test/test_interfaces.cpp | 56 ++++++ .../test/test_msg_initialization.cpp | 45 +++++ rosidl_parser/rosidl_parser/definition.py | 47 ++++- rosidl_parser/rosidl_parser/parser.py | 159 +++++++-------- rosidl_parser/test/msg/MyMessage.idl | 16 ++ rosidl_parser/test/test_parser.py | 45 ++++- .../field_types.h | 2 + .../resource/msg__type_support.c.em | 8 + .../field_types.hpp | 2 + .../resource/msg__type_support.cpp.em | 10 +- 20 files changed, 742 insertions(+), 86 deletions(-) diff --git a/rosidl_generator_c/CMakeLists.txt b/rosidl_generator_c/CMakeLists.txt index 7b9b93be1..f81706aaf 100644 --- a/rosidl_generator_c/CMakeLists.txt +++ b/rosidl_generator_c/CMakeLists.txt @@ -45,6 +45,7 @@ if(BUILD_TESTING) rosidl_generate_interfaces(${PROJECT_NAME}_interfaces ${test_interface_files_MSG_FILES} + ${test_interface_files_IDL_FILES} ADD_LINTER_TESTS SKIP_INSTALL ) diff --git a/rosidl_generator_c/resource/msg__functions.c.em b/rosidl_generator_c/resource/msg__functions.c.em index 0c15b7475..e00caa58b 100644 --- a/rosidl_generator_c/resource/msg__functions.c.em +++ b/rosidl_generator_c/resource/msg__functions.c.em @@ -8,8 +8,11 @@ from rosidl_parser.definition import AbstractWString from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import NamespacedType from rosidl_generator_c import basetype_to_c +from rosidl_generator_c import idl_enumeration_type_to_c_typename +from rosidl_generator_c import idl_enumeration_type_sequence_to_c_typename from rosidl_generator_c import idl_structure_type_sequence_to_c_typename from rosidl_generator_c import idl_structure_type_to_c_include_prefix from rosidl_generator_c import idl_structure_type_to_c_typename @@ -69,6 +72,102 @@ for member in message.structure.members: @[ end for]@ @[end if]@ @#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +@####################################################################### +@# message enums functions +@####################################################################### +@[for enum in message.enumerations]@ +@{ +__enum_typename = idl_enumeration_type_to_c_typename(enum.enumeration_type) +__enum_sequence_typename = idl_enumeration_type_sequence_to_c_typename(enum.enumeration_type) +}@ +bool +@(__enum_sequence_typename)__init(@(__enum_sequence_typename) * sequence, size_t size) +{ + if (!sequence) { + return false; + } + + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + @(__enum_typename) * data = NULL; + + if (size) { + data = (@(__enum_typename) *)allocator.zero_allocate(size, sizeof(@(__enum_typename)), allocator.state); + if (!data) { + return false; + } + } + + sequence->data = data; + sequence->size = size; + sequence->capacity = size; + return true; +} + +void +@(__enum_sequence_typename)__fini(@(__enum_sequence_typename) * sequence) +{ + if (!sequence) { + return; + } + if (sequence->data) { + /* ensure that data and capacity values are consistent */ + assert(sequence->capacity > 0); + free(sequence->data); + sequence->data = NULL; + sequence->size = 0; + sequence->capacity = 0; + } else { + /* ensure that data, size, and capacity values are consistent */ + assert(0 == sequence->size); + assert(0 == sequence->capacity); + } +} + +bool +@(__enum_sequence_typename)__are_equal(const @(__enum_sequence_typename) * lhs, const @(__enum_sequence_typename) * rhs) +{ + if (!lhs || !rhs) { + return false; + } + if (lhs->size != rhs->size) { + return false; + } + for (size_t i = 0; i < lhs->size; ++i) { + if (lhs->data[i] != rhs->data[i]) { + return false; + } + } + return true; +} + +bool +@(__enum_sequence_typename)__copy( + const @(__enum_sequence_typename) * input, + @(__enum_sequence_typename) * output) +{ + if (!input || !output) { + return false; + } + if (output->capacity < input->size) { + const size_t allocation_size = + input->size * sizeof(@(__enum_typename)); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + @(__enum_typename) * data = + (@(__enum_typename) *)allocator.reallocate( + output->data, allocation_size, allocator.state); + if (!data) { + return false; + } + output->data = data; + output->size = input->size; + output->capacity = input->size; + } + for (size_t i = 0; i < input->size; ++i) { + output->data[i] = input->data[i]; + } + return true; +} +@[ end for]@ @####################################################################### @# message functions @@ -185,7 +284,7 @@ for member in message.structure.members: last_label_index += 1 lines.append(' }') lines.append('}') - elif isinstance(member.type, BasicType): + elif isinstance(member.type, (BasicType, EnumerationType)): if member.has_annotation('default'): # set default value of primitive type lines.append('msg->%s = %s;' % (member.name, value_to_c(member.type, member.get_annotation_value('default')['value']))) @@ -224,7 +323,7 @@ for member in message.structure.members: elif isinstance(member.type, AbstractSequence): # finalize the dynamic array lines.append('%s__fini(&msg->%s);' % (idl_type_to_c(member.type), member.name)) - elif not isinstance(member.type, BasicType): + elif not isinstance(member.type, (BasicType, EnumerationType)): # finalize non-array sub messages and strings lines.append('%s__fini(&msg->%s);' % (basetype_to_c(member.type), member.name)) for line in lines: diff --git a/rosidl_generator_c/resource/msg__functions.h.em b/rosidl_generator_c/resource/msg__functions.h.em index 15cd8b42e..8879c232e 100644 --- a/rosidl_generator_c/resource/msg__functions.h.em +++ b/rosidl_generator_c/resource/msg__functions.h.em @@ -1,5 +1,7 @@ @# Included from rosidl_generator_c/resource/idl__functions.h.em @{ +from rosidl_generator_c import idl_enumeration_type_sequence_to_c_typename +from rosidl_generator_c import idl_enumeration_type_to_c_typename from rosidl_generator_c import idl_structure_type_sequence_to_c_typename from rosidl_generator_c import idl_structure_type_to_c_typename from rosidl_generator_c import interface_path_to_string @@ -165,3 +167,60 @@ bool @(array_typename)__copy( const @(array_typename) * input, @(array_typename) * output); + +@####################################################################### +@# message enums functions +@####################################################################### +@[for enum in message.enumerations]@ +@{ +__enum_typename = idl_enumeration_type_to_c_typename(enum.enumeration_type) +__enum_sequence_typename = idl_enumeration_type_sequence_to_c_typename(enum.enumeration_type) +}@ +/// Initialize sequence of @(__enum_typename). +/** + * It allocates the memory for the number of elements. + * \param[in,out] sequence The allocated sequence pointer. + * \param[in] size The size / capacity of the sequence. + * \return true if initialization was successful, otherwise false + * If the sequence pointer is valid and the size is zero it is guaranteed + # to return true. + */ +ROSIDL_GENERATOR_C_PUBLIC_@(package_name) +bool +@(__enum_sequence_typename)__init(@(__enum_sequence_typename) * sequence, size_t size); + +/// Finalize sequence of @(__enum_typename). +/** + * \param[in,out] sequence The allocated sequence pointer. + */ +ROSIDL_GENERATOR_C_PUBLIC_@(package_name) +void +@(__enum_sequence_typename)__fini(@(__enum_sequence_typename) * sequence); + +/// Check for @(__enum_typename) array equality. +/** + * \param[in] lhs The array on the left hand side of the equality operator. + * \param[in] rhs The array on the right hand side of the equality operator. + * \return true if message arrays are equal in size and content, otherwise false. + */ +ROSIDL_GENERATOR_C_PUBLIC_@(package_name) +bool +@(__enum_sequence_typename)__are_equal(const @(__enum_sequence_typename) * lhs, const @(__enum_sequence_typename) * rhs); + +/// Copy an array of @(__enum_typename). +/** + * This function performs a deep copy, as opposed to the shallow copy that + * plain assignment yields. + * + * \param[in] input The source array pointer. + * \param[out] output The target array pointer, which must + * have been initialized before calling this function. + * \return true if successful, or false if either pointer + * is null or memory allocation fails. + */ +ROSIDL_GENERATOR_C_PUBLIC_@(package_name) +bool +@(__enum_sequence_typename)__copy( + const @(__enum_sequence_typename) * input, + @(__enum_sequence_typename) * output); +@[ end for]@ diff --git a/rosidl_generator_c/resource/msg__struct.h.em b/rosidl_generator_c/resource/msg__struct.h.em index ad4b53b1e..76cdc9e10 100644 --- a/rosidl_generator_c/resource/msg__struct.h.em +++ b/rosidl_generator_c/resource/msg__struct.h.em @@ -9,12 +9,15 @@ from rosidl_parser.definition import BasicType from rosidl_parser.definition import BOOLEAN_TYPE from rosidl_parser.definition import BoundedSequence from rosidl_parser.definition import CHARACTER_TYPES +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import FLOATING_POINT_TYPES from rosidl_parser.definition import INTEGER_TYPES from rosidl_parser.definition import NamespacedType from rosidl_parser.definition import OCTET_TYPE from rosidl_generator_c import basetype_to_c from rosidl_generator_c import idl_declaration_to_c +from rosidl_generator_c import idl_enumeration_type_to_c_typename +from rosidl_generator_c import idl_enumeration_type_sequence_to_c_typename from rosidl_generator_c import idl_structure_type_sequence_to_c_typename from rosidl_generator_c import idl_structure_type_to_c_include_prefix from rosidl_generator_c import idl_structure_type_to_c_typename @@ -140,6 +143,30 @@ enum @[end if]@ @#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +// Enums defined in the message +@[for enum in message.enumerations]@ +// Enum defined in @(interface_path_to_string(interface_path)) in the package @(package_name). +typedef enum @(idl_enumeration_type_to_c_typename(enum.enumeration_type)) +{ +@[for enumerator in enum.enumerators]@ + @(enumerator), +@[end for]@ +} @(idl_enumeration_type_to_c_typename(enum.enumeration_type)); + +// Struct for a sequence of @(idl_enumeration_type_to_c_typename(enum.enumeration_type)). +typedef struct @(idl_enumeration_type_sequence_to_c_typename(enum.enumeration_type)) +{ + @(idl_enumeration_type_to_c_typename(enum.enumeration_type)) * data; + /// The number of valid items in data + size_t size; + /// The number of allocated items in data + size_t capacity; +} @(idl_enumeration_type_sequence_to_c_typename(enum.enumeration_type)); +@[ end for]@ +@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + @#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< /// Struct defined in @(interface_path_to_string(interface_path)) in the package @(package_name). @{comments = message.structure.get_comment_lines()}@ diff --git a/rosidl_generator_c/rosidl_generator_c/__init__.py b/rosidl_generator_c/rosidl_generator_c/__init__.py index 838cc3766..45d3f46d6 100644 --- a/rosidl_generator_c/rosidl_generator_c/__init__.py +++ b/rosidl_generator_c/rosidl_generator_c/__init__.py @@ -22,6 +22,7 @@ from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import CHARACTER_TYPES +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import NamespacedType from rosidl_parser.definition import OCTET_TYPE @@ -69,6 +70,14 @@ def prefix_with_bom_if_necessary(content): } +def idl_enumeration_type_to_c_typename(enumeration_type): + return '__'.join(enumeration_type.namespaced_name()) + + +def idl_enumeration_type_sequence_to_c_typename(enumeration_type): + return idl_enumeration_type_to_c_typename(enumeration_type) + '__Sequence' + + def idl_structure_type_to_c_include_prefix(namespaced_type, subdirectory=None): parts = [ convert_camel_case_to_lower_case_underscore(x) @@ -148,6 +157,8 @@ def basetype_to_c(basetype): return 'rosidl_runtime_c__U16String' if isinstance(basetype, NamespacedType): return idl_structure_type_to_c_typename(basetype) + if isinstance(basetype, EnumerationType): + return idl_enumeration_type_to_c_typename(basetype) assert False, str(basetype) @@ -161,6 +172,9 @@ def value_to_c(type_, value): if isinstance(type_, AbstractWString): return 'u"%s"' % escape_wstring(value) + if isinstance(type_, EnumerationType): + return '%s' % value + return basic_value_to_c(type_, value) diff --git a/rosidl_generator_c/test/test_interfaces.c b/rosidl_generator_c/test/test_interfaces.c index ab79527e4..ad7ee03bd 100644 --- a/rosidl_generator_c/test/test_interfaces.c +++ b/rosidl_generator_c/test/test_interfaces.c @@ -25,6 +25,7 @@ #include "rosidl_runtime_c/string_functions.h" #include "rosidl_runtime_c/u16string_functions.h" +#include "rosidl_generator_c/idl/enums_message.h" #include "rosidl_generator_c/msg/arrays.h" #include "rosidl_generator_c/msg/defaults.h" #include "rosidl_generator_c/msg/constants.h" @@ -89,6 +90,11 @@ int test_nested(void); int test_strings(void); int test_unbounded_sequences(void); int test_wstrings(void); +int test_enum(void); +int test_enum_default_value(void); +int test_enum_static_array(void); +int test_enum_bounded_array(void); +int test_enum_dynamic_array(void); int main(void) { @@ -143,6 +149,56 @@ int main(void) fprintf(stderr, "test_multi_nested() FAILED\n"); rc++; } + printf("Testing enum...\n"); + if (test_enum()) { + fprintf(stderr, "test_enum() FAILED\n"); + rc++; + } + printf("Testing enum default value...\n"); + if (test_enum_default_value()) { + fprintf(stderr, "test_enum_default_value() FAILED\n"); + rc++; + } + printf("Testing enum static array...\n"); + if (test_enum_static_array()) { + fprintf(stderr, "test_enum_static_array() FAILED\n"); + rc++; + } + printf("Testing enum bounded array...\n"); + if (test_enum_bounded_array()) { + fprintf(stderr, "test_enum_bounded_array() FAILED\n"); + rc++; + } + printf("Testing enum dynamic array...\n"); + if (test_enum_dynamic_array()) { + fprintf(stderr, "test_enum_dynamic_array() FAILED\n"); + rc++; + } + printf("Testing enum...\n"); + if (test_enum()) { + fprintf(stderr, "test_enum() FAILED\n"); + rc++; + } + printf("Testing enum default value...\n"); + if (test_enum_default_value()) { + fprintf(stderr, "test_enum_default_value() FAILED\n"); + rc++; + } + printf("Testing enum static array...\n"); + if (test_enum_static_array()) { + fprintf(stderr, "test_enum_static_array() FAILED\n"); + rc++; + } + printf("Testing enum bounded array...\n"); + if (test_enum_bounded_array()) { + fprintf(stderr, "test_enum_bounded_array() FAILED\n"); + rc++; + } + printf("Testing enum dynamic array...\n"); + if (test_enum_dynamic_array()) { + fprintf(stderr, "test_enum_dynamic_array() FAILED\n"); + rc++; + } if (rc != 0) { fprintf(stderr, "Some tests failed!\n"); } else { @@ -1298,3 +1354,135 @@ int test_arrays() rosidl_generator_c__msg__Arrays__destroy(arr); return 0; } + +/** + * Test message with enum type. + */ +int test_enum(void) +{ + // expected enumerators + (void)ENUMERATOR1; + (void)ENUMERATOR2; + + test_msgs__idl__EnumsMessage msg; + msg.enum_value = ENUMERATOR2; + EXPECT_EQ(ENUMERATOR2, msg.enum_value); + + return 0; +} + +/** + * Test enum default value. + */ +int test_enum_default_value(void) +{ + test_msgs__idl__EnumsMessage * msg = NULL; + + msg = test_msgs__idl__EnumsMessage__create(); + EXPECT_NE(msg, NULL); + + EXPECT_EQ(ENUMERATOR2, msg->enum_default_value); + + test_msgs__idl__EnumsMessage__destroy(msg); + + return 0; +} + +/** + * Test enum static array. + */ +int test_enum_static_array(void) +{ + int i; + test_msgs__idl__EnumsMessage * arrays = NULL; + arrays = test_msgs__idl__EnumsMessage__create(); + EXPECT_NE(NULL, arrays); + + for (i = 0; i < ARR_SIZE; i++) { + if (0 == (i % 2)) { + arrays->static_array_values[i] = ENUMERATOR1; + } else { + arrays->static_array_values[i] = ENUMERATOR2; + } + } + for (i = 0; i < ARR_SIZE; i++) { + if (0 == (i % 2)) { + EXPECT_EQ(ENUMERATOR1, arrays->static_array_values[i]); + } else { + EXPECT_EQ(ENUMERATOR2, arrays->static_array_values[i]); + } + } + + test_msgs__idl__EnumsMessage__destroy(arrays); + + return 0; +} + +/** + * Test enum bounded array. + */ +int test_enum_bounded_array(void) +{ + int res; + int i; + test_msgs__idl__EnumsMessage * arrays = NULL; + arrays = test_msgs__idl__EnumsMessage__create(); + EXPECT_NE(NULL, arrays); + + res = test_msgs__idl__EnumsMessage__SomeEnum__Sequence__init( + &arrays->bounded_array_values, ARR_SIZE); + EXPECT_EQ(true, res); + for (i = 0; i < ARR_SIZE; i++) { + if (0 == (i % 2)) { + arrays->bounded_array_values.data[i] = ENUMERATOR1; + } else { + arrays->bounded_array_values.data[i] = ENUMERATOR2; + } + } + for (i = 0; i < ARR_SIZE; i++) { + if (0 == (i % 2)) { + EXPECT_EQ(ENUMERATOR1, arrays->bounded_array_values.data[i]); + } else { + EXPECT_EQ(ENUMERATOR2, arrays->bounded_array_values.data[i]); + } + } + + test_msgs__idl__EnumsMessage__destroy(arrays); + + return 0; +} + +/** + * Test enum dynamic array. + */ +int test_enum_dynamic_array(void) +{ + int res; + int i; + test_msgs__idl__EnumsMessage * arrays = NULL; + arrays = test_msgs__idl__EnumsMessage__create(); + EXPECT_NE(NULL, arrays); + + // dynamic array + res = test_msgs__idl__EnumsMessage__SomeEnum__Sequence__init( + &arrays->dynamic_array_values, ARR_SIZE); + EXPECT_EQ(true, res); + for (i = 0; i < ARR_SIZE; i++) { + if (0 == (i % 2)) { + arrays->dynamic_array_values.data[i] = ENUMERATOR1; + } else { + arrays->dynamic_array_values.data[i] = ENUMERATOR2; + } + } + for (i = 0; i < ARR_SIZE; i++) { + if (0 == (i % 2)) { + EXPECT_EQ(ENUMERATOR1, arrays->dynamic_array_values.data[i]); + } else { + EXPECT_EQ(ENUMERATOR2, arrays->dynamic_array_values.data[i]); + } + } + + test_msgs__idl__EnumsMessage__destroy(arrays); + + return 0; +} diff --git a/rosidl_generator_cpp/CMakeLists.txt b/rosidl_generator_cpp/CMakeLists.txt index 6097af81b..59078ab7d 100644 --- a/rosidl_generator_cpp/CMakeLists.txt +++ b/rosidl_generator_cpp/CMakeLists.txt @@ -29,6 +29,7 @@ if(BUILD_TESTING) rosidl_generate_interfaces(${PROJECT_NAME} ${test_interface_files_MSG_FILES} + ${test_interface_files_IDL_FILES} ${test_interface_files_SRV_FILES} ADD_LINTER_TESTS SKIP_INSTALL diff --git a/rosidl_generator_cpp/resource/msg__struct.hpp.em b/rosidl_generator_cpp/resource/msg__struct.hpp.em index 5f2e10b38..d24fe6cd7 100644 --- a/rosidl_generator_cpp/resource/msg__struct.hpp.em +++ b/rosidl_generator_cpp/resource/msg__struct.hpp.em @@ -234,6 +234,16 @@ non_defaulted_zero_initialized_members = [ @[end if]@ } + // enums +@[for enum in message.enumerations]@ + enum class @(enum.enumeration_type.name) + { +@[ for enumerator in enum.enumerators]@ + @(enumerator), +@[ end for]@ + }; +@[end for]@ + // field types and members @[for member in message.structure.members]@ using _@(member.name)_type = diff --git a/rosidl_generator_cpp/resource/msg__traits.hpp.em b/rosidl_generator_cpp/resource/msg__traits.hpp.em index 3243d99d7..e2f60f2f3 100644 --- a/rosidl_generator_cpp/resource/msg__traits.hpp.em +++ b/rosidl_generator_cpp/resource/msg__traits.hpp.em @@ -8,6 +8,7 @@ from rosidl_parser.definition import AbstractGenericString from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence from rosidl_parser.definition import EMPTY_STRUCTURE_REQUIRED_MEMBER_NAME +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import NamespacedType from rosidl_parser.definition import AbstractSequence from rosidl_parser.definition import UnboundedSequence @@ -91,6 +92,10 @@ inline void to_flow_style_yaml( @[ elif isinstance(member.type, AbstractGenericString)]@ out << "@(member.name): "; rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); +@[ elif isinstance(member.type, EnumerationType)]@ + out << "@(member.name): "; + // TODO(r7vme): create value_to_yaml + out << static_cast(msg.@(member.name)); @[ elif isinstance(member.type, NamespacedType)]@ out << "@(member.name): "; to_flow_style_yaml(msg.@(member.name), out); @@ -109,6 +114,9 @@ inline void to_flow_style_yaml( @[ end if]@ @[ elif isinstance(member.type.value_type, AbstractGenericString)]@ rosidl_generator_traits::value_to_yaml(item, out); +@[ elif isinstance(member.type.value_type, EnumerationType)]@ + // TODO(r7vme): create value_to_yaml + out << static_cast(item); @[ elif isinstance(member.type.value_type, NamespacedType)]@ to_flow_style_yaml(item, out); @[ end if]@ @@ -158,6 +166,11 @@ inline void to_block_style_yaml( out << "@(member.name): "; rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); out << "\n"; +@[ elif isinstance(member.type, EnumerationType)]@ + out << "@(member.name): "; + // TODO(r7vme): create value_to_yaml + out << static_cast(msg.@(member.name)); + out << "\n"; @[ elif isinstance(member.type, NamespacedType)]@ out << "@(member.name):\n"; to_block_style_yaml(msg.@(member.name), out, indentation + 2); @@ -182,6 +195,11 @@ inline void to_block_style_yaml( out << "- "; rosidl_generator_traits::value_to_yaml(item, out); out << "\n"; +@[ elif isinstance(member.type.value_type, EnumerationType)]@ + out << "- "; + // TODO(r7vme): create value_to_yaml + out << static_cast(item); + out << "\n"; @[ elif isinstance(member.type.value_type, NamespacedType)]@ out << "-\n"; to_block_style_yaml(item, out, indentation + 2); diff --git a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py index 9fbbf62f0..cff70ee08 100644 --- a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py +++ b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py @@ -23,6 +23,7 @@ from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import FLOATING_POINT_TYPES from rosidl_parser.definition import NamespacedType from rosidl_parser.definition import UnboundedSequence @@ -95,6 +96,8 @@ def msg_type_only_to_cpp(type_): elif isinstance(type_, NamespacedType): typename = '::'.join(type_.namespaced_name()) cpp_type = typename + '_' + elif isinstance(type_, EnumerationType): + cpp_type = type_.name else: assert False, type_ @@ -191,6 +194,12 @@ def primitive_value_to_cpp(type_, value): if isinstance(type_, AbstractWString): return 'u"%s"' % escape_wstring(value) + if isinstance(type_, EnumerationType): + return '::'.join(type_.namespaced_name() + value) + + if isinstance(type_, EnumerationType): + return '::'.join(type_.namespaced_name() + value) + if type_.typename == 'boolean': return 'true' if value else 'false' @@ -310,6 +319,9 @@ def add_member(self, member): for val in default_value: member.default_value.append( primitive_value_to_cpp(field.type.value_type, val)) + elif isinstance(field.type.value_type, EnumerationType): + member.zero_value = [ + 'static_cast<' + field.type.value_type.name + '>(0)'] * field.type.size else: member.zero_value = [] member.zero_need_array_override = True @@ -330,6 +342,11 @@ def add_member(self, member): member.default_value = primitive_value_to_cpp( field.type, field.get_annotation_value('default')['value']) + elif isinstance(field.type, EnumerationType): + member.zero_value = 'static_cast<' + field.type.name + '>(0)' + if field.has_annotation('default'): + annotation_value = field.get_annotation_value('default')['value'] + member.default_value = '::'.join([field.type.name, annotation_value]) else: init_list.append(field.name + '(_init)') alloc_list.append(field.name + '(_alloc, _init)') diff --git a/rosidl_generator_cpp/test/test_interfaces.cpp b/rosidl_generator_cpp/test/test_interfaces.cpp index 7fb2ca921..e2e0ccce0 100644 --- a/rosidl_generator_cpp/test/test_interfaces.cpp +++ b/rosidl_generator_cpp/test/test_interfaces.cpp @@ -22,6 +22,7 @@ #include #include "test_array_generator.hpp" +#include "rosidl_generator_cpp/idl/enums_message.hpp" #include "rosidl_generator_cpp/msg/arrays.hpp" #include "rosidl_generator_cpp/msg/basic_types.hpp" #include "rosidl_generator_cpp/msg/bounded_sequences.hpp" @@ -537,3 +538,58 @@ TEST(Test_messages, Test_string_array_static) { message, string_values_default, std::string, ARRAY_SIZE, \ 0, UINT32_MAX, 0, UINT16_MAX) } + +TEST(Test_messages, test_message_with_enumeration) { + using MsgType = test_msgs::idl::EnumsMessage; + MsgType msg; + msg.enum_value = MsgType::SomeEnum::ENUMERATOR2; + + ASSERT_EQ(msg.enum_value, MsgType::SomeEnum::ENUMERATOR2); +} + +TEST(Test_messages, test_enum_default_value) { + test_msgs::idl::EnumsMessage msg; + auto expected_value = test_msgs::idl::EnumsMessage::SomeEnum::ENUMERATOR2; + + ASSERT_EQ(expected_value, msg.enum_default_value); +} + +TEST(Test_messages, test_bounded_enum_array) { + using MsgType = test_msgs::idl::EnumsMessage; + using EnumType = MsgType::SomeEnum; + + MsgType msg; + rosidl_runtime_cpp::BoundedVector expected_array; + expected_array.resize(ARRAY_SIZE); + std::fill(expected_array.begin(), expected_array.end(), EnumType::ENUMERATOR2); + msg.bounded_array_values.resize(ARRAY_SIZE); + std::copy_n(expected_array.begin(), ARRAY_SIZE, msg.bounded_array_values.begin()); + + EXPECT_EQ(expected_array, msg.bounded_array_values); +} + +TEST(Test_messages, test_unbounded_enum_array) { + using MsgType = test_msgs::idl::EnumsMessage; + using EnumType = MsgType::SomeEnum; + + MsgType msg; + std::vector expected_array; + expected_array.resize(ARRAY_SIZE); + std::fill(expected_array.begin(), expected_array.end(), EnumType::ENUMERATOR2); + msg.dynamic_array_values.resize(ARRAY_SIZE); + std::copy_n(expected_array.begin(), ARRAY_SIZE, msg.dynamic_array_values.begin()); + + EXPECT_EQ(expected_array, msg.dynamic_array_values); +} + +TEST(Test_messages, test_static_enum_array) { + using MsgType = test_msgs::idl::EnumsMessage; + using EnumType = MsgType::SomeEnum; + + MsgType msg; + std::array expected_array; + std::fill(expected_array.begin(), expected_array.end(), EnumType::ENUMERATOR2); + std::copy_n(expected_array.begin(), ARRAY_SIZE, msg.static_array_values.begin()); + + EXPECT_EQ(expected_array, msg.static_array_values); +} diff --git a/rosidl_generator_cpp/test/test_msg_initialization.cpp b/rosidl_generator_cpp/test/test_msg_initialization.cpp index c37b82165..e3a6dbad6 100644 --- a/rosidl_generator_cpp/test/test_msg_initialization.cpp +++ b/rosidl_generator_cpp/test/test_msg_initialization.cpp @@ -18,6 +18,7 @@ #include +#include "rosidl_generator_cpp/idl/enums_message.hpp" #include "rosidl_generator_cpp/msg/defaults.hpp" #include "rosidl_generator_cpp/msg/bounded_sequences.hpp" @@ -216,3 +217,47 @@ TEST(Test_msg_initialization, skip_constructor) { return "" == i; })); } + +TEST(Test_msg_initialization, message_with_enum_initialization) { + using EnumsMessage = test_msgs::idl::EnumsMessage; + + auto zero_value = static_cast(0); + + { + EnumsMessage msg; + ASSERT_EQ(msg.enum_value, zero_value); + } + + { + EnumsMessage msg(rosidl_runtime_cpp::MessageInitialization::ALL); + ASSERT_EQ(msg.enum_value, zero_value); + } + + { + EnumsMessage msg(rosidl_runtime_cpp::MessageInitialization::ZERO); + ASSERT_EQ(msg.enum_value, zero_value); + } + + { + char * memory = new char[sizeof(EnumsMessage)]; + ASSERT_NE(memory, nullptr); + std::memset(memory, 0xfe, sizeof(EnumsMessage)); + + EnumsMessage * def = + new(memory) EnumsMessage( + rosidl_runtime_cpp::MessageInitialization::SKIP); + + // ensures that the memory gets freed even if an ASSERT is raised + SCOPE_EXIT(def->~EnumsMessage(); delete[] memory;); + +#ifndef _WIN32 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif + uint32_t enum_bit_pattern = *reinterpret_cast(&def->enum_value); + ASSERT_EQ(0xfefefefe, enum_bit_pattern); +#ifndef _WIN32 +#pragma GCC diagnostic pop +#endif + } +} diff --git a/rosidl_parser/rosidl_parser/definition.py b/rosidl_parser/rosidl_parser/definition.py index 20b6f7af8..5b50c7e3f 100644 --- a/rosidl_parser/rosidl_parser/definition.py +++ b/rosidl_parser/rosidl_parser/definition.py @@ -88,6 +88,7 @@ EMPTY_STRUCTURE_REQUIRED_MEMBER_NAME = 'structure_needs_at_least_one_member' CONSTANT_MODULE_SUFFIX = '_Constants' +ENUM_MODULE_SUFFIX = '_Enums' SERVICE_REQUEST_MESSAGE_SUFFIX = '_Request' SERVICE_RESPONSE_MESSAGE_SUFFIX = '_Response' @@ -118,6 +119,7 @@ class AbstractNestableType(AbstractType): types. Nestable types are: - BasicType like numerics, character types, boolean and octet + - EnumerationType enumeration defined in the message - NamedType identified by a name which hasn't been resolved yet - NamespacedType which describes another Structure - Strings with any kind of character types, bounded as well as unbounded @@ -193,6 +195,27 @@ def __eq__(self, other): self.namespaces == other.namespaces and self.name == other.name +class EnumerationType(AbstractNestableType): + """A type for enumerations.""" + + __slots__ = ('namespaces', 'name') + + def __init__(self, namespaces: Iterable[str], name: str): + """ + Create a EnumerationType. + + :param namespaces: the names of nested namespaces identifying a + specific scope + :param name: the name of the enumeration + """ + super().__init__() + self.namespaces = namespaces + self.name = name + + def namespaced_name(self) -> Tuple[str, ...]: + return (*self.namespaces, self.name) + + class AbstractGenericString(AbstractNestableType): """The abstract base class of all string types.""" @@ -508,6 +531,23 @@ def __init__(self, namespaced_type: NamespacedType, members=None): self.members = members or [] +class Enumeration(Annotatable): + """A enumeration definition.""" + + __slots__ = ('enumeration_type', 'enumerators') + + def __init__(self, enumeration_type: EnumerationType, enumerators): + """ + Create a Enumeration. + + :param name: the name of enumeration + :param list enumerators: the enumerators + """ + super().__init__() + self.enumeration_type = enumeration_type + self.enumerators = enumerators + + class Include: """An include statement.""" @@ -543,9 +583,9 @@ def __init__(self, name: str, type_: AbstractType, value): class Message: - """A structure containing constants.""" + """A structure containing constants and enumerations.""" - __slots__ = ('structure', 'constants') + __slots__ = ('structure', 'constants', 'enumerations') def __init__(self, structure: Structure): """ @@ -557,6 +597,7 @@ def __init__(self, structure: Structure): assert isinstance(structure, Structure) self.structure = structure self.constants = [] + self.enumerations = [] class Service: @@ -654,7 +695,7 @@ def __init__( # derived types goal_id_type = NamespacedType( - namespaces=['unique_identifier_msgs', 'msg'], name='UUID') + namespaces=['unique_identifier_msgs', 'msg'], name='UUID') goal_service_name = namespaced_type.name + ACTION_GOAL_SERVICE_SUFFIX self.send_goal_service = Service( diff --git a/rosidl_parser/rosidl_parser/parser.py b/rosidl_parser/rosidl_parser/parser.py index 9109c2265..241ba3f82 100644 --- a/rosidl_parser/rosidl_parser/parser.py +++ b/rosidl_parser/rosidl_parser/parser.py @@ -36,6 +36,9 @@ from rosidl_parser.definition import BoundedWString from rosidl_parser.definition import Constant from rosidl_parser.definition import CONSTANT_MODULE_SUFFIX +from rosidl_parser.definition import ENUM_MODULE_SUFFIX +from rosidl_parser.definition import Enumeration +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import IdlContent from rosidl_parser.definition import IdlFile from rosidl_parser.definition import Include @@ -136,45 +139,30 @@ def extract_content_from_ast(tree): else: typedefs[identifier] = abstract_type + enums = {} + enum_dcls = tree.find_data('enum_dcl') + for enum_dcl in enum_dcls: + module_name = get_module_identifier_values(tree, enum_dcl)[-1] + module_enums = enums.setdefault(module_name, []) + enum_identifier = get_first_identifier_value(enum_dcl) + # namespaces are empty, because they filled later with message namespaces + module_enums.append(Enumeration( + EnumerationType(namespaces=[], name=enum_identifier), + get_enumerators_from_enumeration(enum_dcl))) + struct_defs = list(tree.find_data('struct_def')) if len(struct_defs) == 1: - msg = Message(Structure(NamespacedType( - namespaces=get_module_identifier_values(tree, struct_defs[0]), - name=get_child_identifier_value(struct_defs[0])))) - annotations = get_annotations(struct_defs[0]) - msg.structure.annotations += annotations - add_message_members(msg, struct_defs[0]) - resolve_typedefed_names(msg.structure, typedefs) - constant_module_name = msg.structure.namespaced_type.name + \ - CONSTANT_MODULE_SUFFIX - if constant_module_name in constants: - msg.constants += constants[constant_module_name] + msg = create_message(tree, struct_defs[0], constants, enums, typedefs) content.elements.append(msg) elif len(struct_defs) == 2: - request = Message(Structure(NamespacedType( - namespaces=get_module_identifier_values(tree, struct_defs[0]), - name=get_child_identifier_value(struct_defs[0])))) - assert request.structure.namespaced_type.name.endswith( + assert str(get_child_identifier_value(struct_defs[0])).endswith( SERVICE_REQUEST_MESSAGE_SUFFIX) - add_message_members(request, struct_defs[0]) - resolve_typedefed_names(request.structure, typedefs) - constant_module_name = \ - request.structure.namespaced_type.name + CONSTANT_MODULE_SUFFIX - if constant_module_name in constants: - request.constants += constants[constant_module_name] - - response = Message(Structure(NamespacedType( - namespaces=get_module_identifier_values(tree, struct_defs[1]), - name=get_child_identifier_value(struct_defs[1])))) - assert response.structure.namespaced_type.name.endswith( + assert str(get_child_identifier_value(struct_defs[1])).endswith( SERVICE_RESPONSE_MESSAGE_SUFFIX) - add_message_members(response, struct_defs[1]) - resolve_typedefed_names(response.structure, typedefs) - constant_module_name = \ - response.structure.namespaced_type.name + CONSTANT_MODULE_SUFFIX - if constant_module_name in constants: - response.constants += constants[constant_module_name] + + request = create_message(tree, struct_defs[0], constants, enums, typedefs) + response = create_message(tree, struct_defs[1], constants, enums, typedefs) assert request.structure.namespaced_type.namespaces == \ response.structure.namespaced_type.namespaces @@ -192,28 +180,13 @@ def extract_content_from_ast(tree): content.elements.append(srv) elif len(struct_defs) == 3: - goal = Message(Structure(NamespacedType( - namespaces=get_module_identifier_values(tree, struct_defs[0]), - name=get_child_identifier_value(struct_defs[0])))) - assert goal.structure.namespaced_type.name.endswith(ACTION_GOAL_SUFFIX) - add_message_members(goal, struct_defs[0]) - resolve_typedefed_names(goal.structure, typedefs) - constant_module_name = \ - goal.structure.namespaced_type.name + CONSTANT_MODULE_SUFFIX - if constant_module_name in constants: - goal.constants += constants[constant_module_name] - - result = Message(Structure(NamespacedType( - namespaces=get_module_identifier_values(tree, struct_defs[1]), - name=get_child_identifier_value(struct_defs[1])))) - assert result.structure.namespaced_type.name.endswith( - ACTION_RESULT_SUFFIX) - add_message_members(result, struct_defs[1]) - resolve_typedefed_names(result.structure, typedefs) - constant_module_name = \ - result.structure.namespaced_type.name + CONSTANT_MODULE_SUFFIX - if constant_module_name in constants: - result.constants += constants[constant_module_name] + assert str(get_child_identifier_value(struct_defs[0])).endswith(ACTION_GOAL_SUFFIX) + assert str(get_child_identifier_value(struct_defs[1])).endswith(ACTION_RESULT_SUFFIX) + assert str(get_child_identifier_value(struct_defs[2])).endswith(ACTION_FEEDBACK_SUFFIX) + + goal = create_message(tree, struct_defs[0], constants, enums, typedefs) + result = create_message(tree, struct_defs[1], constants, enums, typedefs) + feedback = create_message(tree, struct_defs[2], constants, enums, typedefs) assert goal.structure.namespaced_type.namespaces == \ result.structure.namespaced_type.namespaces @@ -223,24 +196,11 @@ def extract_content_from_ast(tree): :-len(ACTION_RESULT_SUFFIX)] assert goal_basename == result_basename - feedback_message = Message(Structure(NamespacedType( - namespaces=get_module_identifier_values(tree, struct_defs[2]), - name=get_child_identifier_value(struct_defs[2])))) - assert feedback_message.structure.namespaced_type.name.endswith( - ACTION_FEEDBACK_SUFFIX) - add_message_members(feedback_message, struct_defs[2]) - resolve_typedefed_names(feedback_message.structure, typedefs) - constant_module_name = \ - feedback_message.structure.namespaced_type.name + \ - CONSTANT_MODULE_SUFFIX - if constant_module_name in constants: - feedback_message.constants += constants[constant_module_name] - action = Action( NamespacedType( namespaces=goal.structure.namespaced_type.namespaces, name=goal_basename), - goal, result, feedback_message) + goal, result, feedback) all_includes = content.get_elements_of_type(Include) unique_include_locators = { @@ -259,28 +219,62 @@ def extract_content_from_ast(tree): return content -def resolve_typedefed_names(structure, typedefs): +def create_message(tree, struct_def, constants, enums, typedefs): + msg = Message(Structure(NamespacedType( + namespaces=get_module_identifier_values(tree, struct_def), + name=get_child_identifier_value(struct_def)))) + annotations = get_annotations(struct_def) + msg.structure.annotations += annotations + add_message_members(msg, struct_def) + + constant_module_name = msg.structure.namespaced_type.name + \ + CONSTANT_MODULE_SUFFIX + if constant_module_name in constants: + msg.constants += constants[constant_module_name] + + type_names_mapping = typedefs.copy() + enum_module_name = msg.structure.namespaced_type.name + ENUM_MODULE_SUFFIX + if enum_module_name in enums: + for enum in enums[enum_module_name]: + # use struct namespaces for enum + enum.enumeration_type.namespaces = list( + msg.structure.namespaced_type.namespaced_name()) + + # required to resolve short type names + assert enum.enumeration_type.name not in type_names_mapping, 'Fail' + type_names_mapping[enum.enumeration_type.name] = enum.enumeration_type + + msg.enumerations.append(enum) + + resolve_type_names(msg.structure, type_names_mapping) + + return msg + + +def resolve_type_names(structure, type_names_mapping): for member in structure.members: type_ = member.type if isinstance(type_, AbstractNestedType): type_ = type_.value_type + assert isinstance(type_, AbstractType) + if isinstance(type_, NamedType): - assert type_.name in typedefs, 'Unknown named type: ' + type_.name - typedefed_type = typedefs[type_.name] + assert type_.name in type_names_mapping, 'Unknown named type: ' + type_.name + resolved_type = type_names_mapping[type_.name] # second level of indirection for arrays of structures - if isinstance(typedefed_type, AbstractNestedType): - if isinstance(typedefed_type.value_type, NamedType): - assert typedefed_type.value_type.name in typedefs, \ - 'Unknown named type: ' + typedefed_type.value_type.name - typedefed_type.value_type = \ - typedefs[typedefed_type.value_type.name] + if isinstance(resolved_type, AbstractNestedType): + if isinstance(resolved_type.value_type, NamedType): + assert resolved_type.value_type.name in type_names_mapping, \ + 'Unknown named type: ' + resolved_type.value_type.name + resolved_type.value_type = \ + type_names_mapping[resolved_type.value_type.name] if isinstance(member.type, AbstractNestedType): - member.type.value_type = typedefed_type + member.type.value_type = resolved_type else: - member.type = typedefed_type + member.type = resolved_type def get_first_identifier_value(tree): @@ -299,6 +293,13 @@ def get_child_identifier_value(tree): return None +def get_enumerators_from_enumeration(tree): + """Get all enumerators from enumeration.""" + assert isinstance(tree.children[0], Token) + # extract all identifiers and remove first one, which is enum name + return list(tree.scan_values(_find_tokens('IDENTIFIER')))[1:] + + def _find_tokens(token_type): def find(t): if isinstance(t, Token): diff --git a/rosidl_parser/test/msg/MyMessage.idl b/rosidl_parser/test/msg/MyMessage.idl index f2d3cfa61..605d93dfe 100644 --- a/rosidl_parser/test/msg/MyMessage.idl +++ b/rosidl_parser/test/msg/MyMessage.idl @@ -13,6 +13,14 @@ module rosidl_parser { const string EMPTY_STRING_CONSTANT = ""; }; + module MyMessage_Enums { + enum MyEnum { + ENUMERATOR1, + ENUMERATOR2, + ENUMERATOR3 + }; + }; + @verbatim ( language="comment", text="Documentation of MyMessage." "Adjacent string literal." ) @transfer_mode(SHMEM_REF) struct MyMessage { @@ -81,6 +89,14 @@ module rosidl_parser { float fixed_frac_only; @default ( value=7d ) float fixed_int_only; + + // Tests for enumerations + MyEnum enum_value; + MyEnum static_array_enum_values[3]; + sequence dynamic_array_enum_values; + sequence bounded_array_enum_values; + @default (value="ENUMERATOR1") + MyEnum enum_default_value; }; }; }; diff --git a/rosidl_parser/test/test_parser.py b/rosidl_parser/test/test_parser.py index 614b86c09..78f757e57 100644 --- a/rosidl_parser/test/test_parser.py +++ b/rosidl_parser/test/test_parser.py @@ -22,6 +22,8 @@ from rosidl_parser.definition import BoundedSequence from rosidl_parser.definition import BoundedString from rosidl_parser.definition import BoundedWString +from rosidl_parser.definition import Enumeration +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import IdlLocator from rosidl_parser.definition import Include from rosidl_parser.definition import Message @@ -57,6 +59,41 @@ def test_message_parser_includes(message_idl_file): assert includes[1].locator == 'pkgname/msg/OtherMessage.idl' +def test_enums_parsing(message_idl_file): + messages = message_idl_file.content.get_elements_of_type(Message) + assert len(messages) == 1 + + enums = messages[0].enumerations + assert len(enums) == 1 + assert isinstance(enums[0], Enumeration) + assert enums[0].enumeration_type.name == 'MyEnum' + assert enums[0].enumeration_type.namespaces == [ + 'rosidl_parser', 'msg', 'MyMessage'] + assert enums[0].enumerators == [ + 'ENUMERATOR1', 'ENUMERATOR2', 'ENUMERATOR3'] + + structure = messages[0].structure + assert isinstance(structure.members[45].type, EnumerationType) + assert structure.members[45].type.name == 'MyEnum' + assert structure.members[45].name == 'enum_value' + + assert isinstance(structure.members[46].type, Array) + assert isinstance(structure.members[46].type.value_type, EnumerationType) + assert structure.members[46].type.value_type.name == 'MyEnum' + assert structure.members[46].type.size == 3 + assert structure.members[46].name == 'static_array_enum_values' + + assert isinstance(structure.members[47].type, UnboundedSequence) + assert isinstance(structure.members[47].type.value_type, EnumerationType) + assert structure.members[47].type.value_type.name == 'MyEnum' + assert structure.members[47].name == 'dynamic_array_enum_values' + + assert isinstance(structure.members[48].type, BoundedSequence) + assert isinstance(structure.members[48].type.value_type, EnumerationType) + assert structure.members[48].type.value_type.name == 'MyEnum' + assert structure.members[48].name == 'bounded_array_enum_values' + + def test_message_parser_structure(message_idl_file): messages = message_idl_file.content.get_elements_of_type(Message) assert len(messages) == 1 @@ -99,7 +136,7 @@ def test_message_parser_structure(message_idl_file): structure = messages[0].structure assert structure.namespaced_type.namespaces == ['rosidl_parser', 'msg'] assert structure.namespaced_type.name == 'MyMessage' - assert len(structure.members) == 45 + assert len(structure.members) == 50 assert isinstance(structure.members[0].type, BasicType) assert structure.members[0].type.typename == 'int16' @@ -150,6 +187,12 @@ def test_message_parser_structure(message_idl_file): assert structure.members[31].type.size == 23 assert structure.members[31].name == 'array_short_values' + assert len(structure.members[49].annotations) == 1 + assert structure.members[49].annotations[0].name == 'default' + assert len(structure.members[49].annotations[0].value) == 1 + assert 'value' in structure.members[49].annotations[0].value + assert structure.members[49].annotations[0].value['value'] == 'ENUMERATOR1' + def test_message_parser_annotations(message_idl_file): messages = message_idl_file.content.get_elements_of_type(Message) diff --git a/rosidl_typesupport_introspection_c/include/rosidl_typesupport_introspection_c/field_types.h b/rosidl_typesupport_introspection_c/include/rosidl_typesupport_introspection_c/field_types.h index cd336cd3b..de052c22a 100644 --- a/rosidl_typesupport_introspection_c/include/rosidl_typesupport_introspection_c/field_types.h +++ b/rosidl_typesupport_introspection_c/include/rosidl_typesupport_introspection_c/field_types.h @@ -64,6 +64,8 @@ enum rosidl_typesupport_introspection_c_field_types /// An embedded message type. rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE = 18, + /// An embedded enumeration type. + rosidl_typesupport_introspection_c__ROS_TYPE_ENUM = 19, /// For backward compatibility only. rosidl_typesupport_introspection_c__ROS_TYPE_FLOAT32 = 1, diff --git a/rosidl_typesupport_introspection_c/resource/msg__type_support.c.em b/rosidl_typesupport_introspection_c/resource/msg__type_support.c.em index 5a213df01..31b028936 100644 --- a/rosidl_typesupport_introspection_c/resource/msg__type_support.c.em +++ b/rosidl_typesupport_introspection_c/resource/msg__type_support.c.em @@ -10,6 +10,7 @@ from rosidl_parser.definition import AbstractWString from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import NamespacedType include_parts = [package_name] + list(interface_path.parents[0].parts) + [ @@ -227,6 +228,13 @@ for index, member in enumerate(message.structure.members): print(' %u, // upper bound of string' % (type_.maximum_size if type_.has_maximum_size() else 0)) # const rosidl_generator_c::MessageTypeSupportHandle * members_ print(' NULL, // members of sub message') + elif isinstance(type_, EnumerationType): + # uint8_t type_id_ + print(' rosidl_typesupport_introspection_c__ROS_TYPE_ENUM, // type') + # size_t string_upper_bound + print(' 0, // upper bound of string') + # const rosidl_generator_c::MessageTypeSupportHandle * members_ + print(' NULL, // members of sub message') else: # uint8_t type_id_ print(' rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE, // type') diff --git a/rosidl_typesupport_introspection_cpp/include/rosidl_typesupport_introspection_cpp/field_types.hpp b/rosidl_typesupport_introspection_cpp/include/rosidl_typesupport_introspection_cpp/field_types.hpp index dc21f61f2..089b79fcc 100644 --- a/rosidl_typesupport_introspection_cpp/include/rosidl_typesupport_introspection_cpp/field_types.hpp +++ b/rosidl_typesupport_introspection_cpp/include/rosidl_typesupport_introspection_cpp/field_types.hpp @@ -61,6 +61,8 @@ const uint8_t ROS_TYPE_WSTRING = rosidl_typesupport_introspection_c__ROS_TYPE_WS /// An embedded message type. const uint8_t ROS_TYPE_MESSAGE = rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE; +/// An embedded enumeration type. +const uint8_t ROS_TYPE_ENUM = rosidl_typesupport_introspection_c__ROS_TYPE_ENUM; /// For backward compatibility only. const uint8_t ROS_TYPE_BOOL = rosidl_typesupport_introspection_c__ROS_TYPE_BOOL; diff --git a/rosidl_typesupport_introspection_cpp/resource/msg__type_support.cpp.em b/rosidl_typesupport_introspection_cpp/resource/msg__type_support.cpp.em index 60cc226c4..98e82a1af 100644 --- a/rosidl_typesupport_introspection_cpp/resource/msg__type_support.cpp.em +++ b/rosidl_typesupport_introspection_cpp/resource/msg__type_support.cpp.em @@ -8,6 +8,7 @@ from rosidl_parser.definition import AbstractWString from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import EnumerationType from rosidl_parser.definition import NamespacedType from rosidl_cmake import convert_camel_case_to_lower_case_underscore @@ -77,7 +78,7 @@ elif isinstance(member.type.value_type, AbstractString): type_ = 'std::string' elif isinstance(member.type.value_type, AbstractWString): type_ = 'std::u16string' -elif isinstance(member.type.value_type, NamespacedType): +elif isinstance(member.type.value_type, (NamespacedType, EnumerationType)): type_ = '::'.join(member.type.value_type.namespaced_name()) }@ size_t size_function__@(message.structure.namespaced_type.name)__@(member.name)(const void * untyped_member) @@ -192,6 +193,13 @@ for index, member in enumerate(message.structure.members): print(' %u, // upper bound of string' % (type_.maximum_size if type_.has_maximum_size() else 0)) # const rosidl_generator_c::MessageTypeSupportHandle * members_ print(' nullptr, // members of sub message') + elif isinstance(type_, EnumerationType): + # uint8_t type_id_ + print(' rosidl_typesupport_introspection_c__ROS_TYPE_ENUM, // type') + # size_t string_upper_bound + print(' 0, // upper bound of string') + # const rosidl_generator_c::MessageTypeSupportHandle * members_ + print(' nullptr, // members of sub message') else: # uint8_t type_id_ print(' ::rosidl_typesupport_introspection_cpp::ROS_TYPE_MESSAGE, // type')