From 94e7300002c639c394672a93d5dabf3066abc153 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 09:13:06 -0700 Subject: [PATCH 01/21] init Signed-off-by: Michael Carlstrom --- rosidl_parser/test/msg/MyMessage.idl | 4 ++++ rosidl_parser/test/test_parser.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/rosidl_parser/test/msg/MyMessage.idl b/rosidl_parser/test/msg/MyMessage.idl index f2d3cfa61..0348c546c 100644 --- a/rosidl_parser/test/msg/MyMessage.idl +++ b/rosidl_parser/test/msg/MyMessage.idl @@ -81,6 +81,10 @@ module rosidl_parser { float fixed_frac_only; @default ( value=7d ) float fixed_int_only; + + // Optional test + @optional + int32 optional_int; }; }; }; diff --git a/rosidl_parser/test/test_parser.py b/rosidl_parser/test/test_parser.py index 61b164742..e7a66f70c 100644 --- a/rosidl_parser/test/test_parser.py +++ b/rosidl_parser/test/test_parser.py @@ -134,7 +134,7 @@ def test_message_parser_structure(message_idl_file: IdlFile) -> None: 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) == 46 assert isinstance(structure.members[0].type, BasicType) assert structure.members[0].type.typename == 'int16' @@ -304,6 +304,12 @@ def test_message_parser_annotations(message_idl_file: IdlFile) -> None: assert len(structure.members[44].annotations) == 1 assert structure.members[44].annotations[0].name == 'default' + assert isinstance(structure.members[45].type, BasicType) + assert structure.members[45].type.typename == 'int32' + assert structure.members[45].name == 'optional_int' + assert len(structure.members[45].annotations) == 1 + assert structure.members[45].annotations[0].name == 'optional' + @pytest.fixture(scope='module') def service_idl_file() -> IdlFile: From bc24a86277713aa4337ba48b92de459d4d46f0b5 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 15:24:50 -0700 Subject: [PATCH 02/21] initial implementation Signed-off-by: Michael Carlstrom --- .../resource/idl__struct.hpp.em | 1 + .../resource/msg__struct.hpp.em | 5 ++- .../rosidl_generator_cpp/__init__.py | 38 ++++++++++++++----- rosidl_generator_tests/CMakeLists.txt | 1 + rosidl_generator_tests/msg/OptionalIdl.idl | 17 +++++++++ .../rosidl_generator_cpp/test_msg_builder.cpp | 15 ++++++++ .../test_msg_initialization.cpp | 10 +++++ .../test/rosidl_generator_cpp/test_traits.cpp | 8 ++++ rosidl_parser/rosidl_parser/definition.py | 2 + .../include/rosidl_runtime_cpp/traits.hpp | 11 ++++++ 10 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 rosidl_generator_tests/msg/OptionalIdl.idl diff --git a/rosidl_generator_cpp/resource/idl__struct.hpp.em b/rosidl_generator_cpp/resource/idl__struct.hpp.em index b8f985617..58e42a3ed 100644 --- a/rosidl_generator_cpp/resource/idl__struct.hpp.em +++ b/rosidl_generator_cpp/resource/idl__struct.hpp.em @@ -30,6 +30,7 @@ include_directives = set() #include #include #include +#include #include #include diff --git a/rosidl_generator_cpp/resource/msg__struct.hpp.em b/rosidl_generator_cpp/resource/msg__struct.hpp.em index 6e583888f..85349860c 100644 --- a/rosidl_generator_cpp/resource/msg__struct.hpp.em +++ b/rosidl_generator_cpp/resource/msg__struct.hpp.em @@ -4,6 +4,7 @@ from rosidl_generator_cpp import create_init_alloc_and_member_lists from rosidl_generator_cpp import escape_string from rosidl_generator_cpp import escape_wstring from rosidl_generator_cpp import msg_type_to_cpp +from rosidl_generator_cpp import member_to_cpp from rosidl_generator_cpp import MSG_TYPE_TO_CPP from rosidl_parser.definition import AbstractNestedType from rosidl_parser.definition import AbstractString @@ -247,7 +248,7 @@ non_defaulted_zero_initialized_members = [ // field types and members @[for member in message.structure.members]@ using _@(member.name)_type = - @(msg_type_to_cpp(member.type)); + @(member_to_cpp(member)); _@(member.name)_type @(member.name); @[end for]@ @@ -255,7 +256,7 @@ non_defaulted_zero_initialized_members = [ // setters for named parameter idiom @[ for member in message.structure.members]@ Type & set__@(member.name)( - const @(msg_type_to_cpp(member.type)) & _arg) + const @(member_to_cpp(member)) & _arg) { this->@(member.name) = _arg; return *this; diff --git a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py index 85db597c7..934392895 100644 --- a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py +++ b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py @@ -14,17 +14,22 @@ from ast import literal_eval from typing import List +from typing import Union from rosidl_parser.definition import AbstractGenericString from rosidl_parser.definition import AbstractNestedType from rosidl_parser.definition import AbstractSequence from rosidl_parser.definition import AbstractString +from rosidl_parser.definition import AbstractType 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 FLOATING_POINT_TYPES +from rosidl_parser.definition import Member +from rosidl_parser.definition import Message from rosidl_parser.definition import NamespacedType +from rosidl_parser.definition import OPTIONAL_ANNOTATION from rosidl_parser.definition import UnboundedSequence from rosidl_pycommon import generate_files @@ -76,7 +81,7 @@ def prefix_with_bom_if_necessary(content: str) -> str: } -def msg_type_only_to_cpp(type_): +def msg_type_only_to_cpp(type_: AbstractType) -> str: """ Convert a message type into the C++ declaration, ignoring array types. @@ -103,7 +108,7 @@ def msg_type_only_to_cpp(type_): return cpp_type -def msg_type_to_cpp(type_): +def msg_type_to_cpp(type_: AbstractType) -> str: """ Convert a message type into the C++ declaration, along with the array type. @@ -134,7 +139,16 @@ def msg_type_to_cpp(type_): return cpp_type -def value_to_cpp(type_, value): +def member_to_cpp(member: Member) -> str: + """Convert a member into the C++ declaration. This exists to add optional support.""" + cpp_type = msg_type_to_cpp(member.type) + + if member.has_annotation(OPTIONAL_ANNOTATION): + return f'std::optional<{cpp_type}>' + return cpp_type + + +def value_to_cpp(type_: AbstractType, value) -> str: """ Convert a python value into a string representing that value in C++. @@ -149,7 +163,9 @@ def value_to_cpp(type_, value): """ assert not isinstance(type_, NamespacedType), \ "Could not convert non-primitive type '%s' to CPP" % (type_) - assert value is not None, "Value for type '%s' must not be None" % (type_) + + if value is None: + return 'std::nullopt' if not isinstance(type_, AbstractNestedType): return primitive_value_to_cpp(type_, value) @@ -171,7 +187,7 @@ def value_to_cpp(type_, value): return cpp_value -def primitive_value_to_cpp(type_, value): +def primitive_value_to_cpp(type_: Union[BasicType, AbstractGenericString], value) -> str: """ Convert a python value into a string representing that value in C++. @@ -242,17 +258,17 @@ def default_value_from_type(type_): return 0 -def escape_string(s): +def escape_string(s: str) -> str: s = s.replace('\\', '\\\\') s = s.replace('"', '\\"') return s -def escape_wstring(s): +def escape_wstring(s: str) -> str: return escape_string(s) -def create_init_alloc_and_member_lists(message): +def create_init_alloc_and_member_lists(message: Message): # A Member object represents the information we need to know to initialize # a single member of the class. class Member: @@ -298,7 +314,11 @@ def add_member(self, member): for field in message.structure.members: member = Member(field.name) member.type = field.type - if isinstance(field.type, Array): + if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): + # Use existing logic for default + member.default_value = value_to_cpp(field.type, None) + + elif isinstance(field.type, Array): alloc_list.append(field.name + '(_alloc)') if isinstance(field.type.value_type, BasicType) or \ isinstance(field.type.value_type, AbstractGenericString): diff --git a/rosidl_generator_tests/CMakeLists.txt b/rosidl_generator_tests/CMakeLists.txt index bd858f870..8023bb00d 100644 --- a/rosidl_generator_tests/CMakeLists.txt +++ b/rosidl_generator_tests/CMakeLists.txt @@ -35,6 +35,7 @@ if(BUILD_TESTING) ${test_interface_files_MSG_FILES} ${test_interface_files_SRV_FILES} msg/BasicIdl.idl + msg/OptionalIdl.idl msg/SmallConstant.msg ADD_LINTER_TESTS SKIP_INSTALL diff --git a/rosidl_generator_tests/msg/OptionalIdl.idl b/rosidl_generator_tests/msg/OptionalIdl.idl new file mode 100644 index 000000000..a7cd23cf9 --- /dev/null +++ b/rosidl_generator_tests/msg/OptionalIdl.idl @@ -0,0 +1,17 @@ +module rosidl_generator_tests { + module msg { + struct OptionalIdl { + @optional float optional_float; + + @default ( value=32.0 ) + @optional + float default_optional_float; + + // @optional + // string optional_string; + + // @optional + // short optional_short_array[3]; + }; + }; +}; diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp index 229668c27..4dedc8a77 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp @@ -14,11 +14,14 @@ #include +#include + #include "rosidl_generator_tests/msg/detail/arrays__builder.hpp" #include "rosidl_generator_tests/msg/detail/basic_types__builder.hpp" #include "rosidl_generator_tests/msg/detail/empty__builder.hpp" #include "rosidl_generator_tests/msg/detail/multi_nested__builder.hpp" #include "rosidl_generator_tests/msg/detail/nested__builder.hpp" +#include "rosidl_generator_tests/msg/detail/optional_idl__builder.hpp" TEST(Test_msg_initialization, build) { ::rosidl_generator_tests::msg::BasicTypes basic = @@ -200,4 +203,16 @@ TEST(Test_msg_initialization, build) { rosidl_generator_tests::msg::Empty empty = rosidl_generator_tests::build(); ASSERT_EQ(0, empty.structure_needs_at_least_one_member); + + rosidl_generator_tests::msg::OptionalIdl optional = + rosidl_generator_tests::build() + .optional_float(0.1f) + .default_optional_float(2.0); + // .optional_short_array({{-100, -200, -100}}); + // .optional_string("init"); + + ASSERT_EQ(0.1f, optional.optional_float); + ASSERT_EQ(2.0, optional.default_optional_float); + // ASSERT_EQ({-100, -200, -100}, optional.default_optional_float); + // ASSERT_EQ("init", optional.optional_string) } diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp index 668aed0b0..3d4121143 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp @@ -17,9 +17,11 @@ #include #include +#include #include "rosidl_generator_tests/msg/defaults.hpp" #include "rosidl_generator_tests/msg/bounded_sequences.hpp" +#include "rosidl_generator_tests/msg/optional_idl.hpp" template struct ScopeExit @@ -171,6 +173,14 @@ TEST(Test_msg_initialization, defaults_only_constructor) { ASSERT_EQ(0LL, basic.int64_value); } +TEST(Test_msg_initialization, optional_msg_initialization) { + rosidl_generator_tests::msg::OptionalIdl optional_msg; + ASSERT_EQ(std::nullopt, optional_msg.optional_float); + ASSERT_EQ(32.0, optional_msg.default_optional_float); + // ASSERT_EQ(std::nullopt, optional_msg.optional_short_array); + // ASSERT_EQ(std::nullopt, optional_msg.optional_string); +} + // This is a test to ensure that when the user passes SKIP to the constructor, // it does no initialization. TEST(Test_msg_initialization, skip_constructor) { diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp index 7e16d450c..eefdbbc9e 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp @@ -22,6 +22,7 @@ #include "rosidl_generator_tests/msg/empty.hpp" #include "rosidl_generator_tests/msg/bounded_sequences.hpp" #include "rosidl_generator_tests/msg/nested.hpp" +#include "rosidl_generator_tests/msg/optional_idl.hpp" #include "rosidl_generator_tests/msg/strings.hpp" #include "rosidl_generator_tests/msg/w_strings.hpp" #include "rosidl_generator_tests/srv/empty.hpp" @@ -254,6 +255,13 @@ alignment_check: 0 )", yaml.c_str()); } + + { + const rosidl_generator_tests::msg::OptionalIdl optional_msg; + EXPECT_STREQ( + "optional_float: std::nullopt\ndefault_optional_float: 32.0000\n", + to_yaml(optional_msg).c_str()); + } } TEST(Test_rosidl_generator_traits, to_yaml_flow_style) { diff --git a/rosidl_parser/rosidl_parser/definition.py b/rosidl_parser/rosidl_parser/definition.py index 5873ba470..24036a94a 100644 --- a/rosidl_parser/rosidl_parser/definition.py +++ b/rosidl_parser/rosidl_parser/definition.py @@ -141,6 +141,8 @@ ACTION_RESULT_SERVICE_SUFFIX: Final = '_GetResult' ACTION_FEEDBACK_MESSAGE_SUFFIX: Final = '_FeedbackMessage' +OPTIONAL_ANNOTATION: Final = 'optional' + class AbstractType: """The abstract base class for all types.""" diff --git a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp index 5a4773c66..f50785f7c 100644 --- a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp +++ b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace rosidl_generator_traits @@ -150,6 +151,16 @@ inline void value_to_yaml(const std::u16string & value, std::ostream & out) out << "\""; } +template +inline void value_to_yaml(const std::optional value, std::ostream & out) +{ + if (value.has_value()) { + value_to_yaml(value.value(), out); + } else { + out << "std::nullopt"; + } +} + template inline const char * data_type(); From dbd9c057f0b124018308764e913d3a79b245d24c Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 16:13:21 -0700 Subject: [PATCH 03/21] explicit nullopt default Signed-off-by: Michael Carlstrom --- .../rosidl_generator_cpp/__init__.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py index 934392895..c3ebebb5d 100644 --- a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py +++ b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py @@ -14,6 +14,7 @@ from ast import literal_eval from typing import List +from typing import Optional from typing import Union from rosidl_parser.definition import AbstractGenericString @@ -273,15 +274,15 @@ def create_init_alloc_and_member_lists(message: Message): # a single member of the class. class Member: - def __init__(self, name): + def __init__(self, name: str): self.name = name - self.default_value = None - self.zero_value = None + self.default_value: Optional[str] = None + self.zero_value: Optional[str] = None self.zero_need_array_override = False - self.type = None + self.type: Optional[AbstractType] = None self.num_prealloc = 0 - def same_default_and_zero_value(self, other): + def same_default_and_zero_value(self, other) -> bool: return self.default_value == other.default_value and \ self.zero_value == other.zero_value @@ -292,10 +293,10 @@ def same_default_and_zero_value(self, other): # value). class CommonMemberSet: - def __init__(self): - self.members = [] + def __init__(self) -> None: + self.members: list[Member] = [] - def add_member(self, member): + def add_member(self, member: Member) -> bool: if not self.members or self.members[-1].same_default_and_zero_value(member): self.members.append(member) return True @@ -308,17 +309,14 @@ def add_member(self, member): # initializion in the allocator constructor # member_list - The list of members that we will generate initialization code # for in the body of the constructors - init_list = [] - alloc_list = [] - member_list = [] + init_list: list[str] = [] + alloc_list: list[str] = [] + member_list: list[CommonMemberSet] = [] for field in message.structure.members: member = Member(field.name) member.type = field.type - if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): - # Use existing logic for default - member.default_value = value_to_cpp(field.type, None) - elif isinstance(field.type, Array): + if isinstance(field.type, Array): alloc_list.append(field.name + '(_alloc)') if isinstance(field.type.value_type, BasicType) or \ isinstance(field.type.value_type, AbstractGenericString): @@ -362,4 +360,8 @@ def add_member(self, member): commonset.add_member(member) member_list.append(commonset) + # Override default if optional + if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): + member.default_value = value_to_cpp(field.type, None) + return init_list, alloc_list, member_list From 952690f14243b821d513e9ed8e0900af1fda5977 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 18:23:16 -0700 Subject: [PATCH 04/21] switch default to zero_value Signed-off-by: Michael Carlstrom --- rosidl_generator_cpp/rosidl_generator_cpp/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py index c3ebebb5d..d4175555c 100644 --- a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py +++ b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py @@ -360,8 +360,8 @@ def add_member(self, member: Member) -> bool: commonset.add_member(member) member_list.append(commonset) - # Override default if optional - if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): - member.default_value = value_to_cpp(field.type, None) + # Override zero_value if optional + if field.has_annotation(OPTIONAL_ANNOTATION): + member.zero_value = value_to_cpp(field.type, None) return init_list, alloc_list, member_list From 33b34dfb8cb3fe5bc50e617295464b7496c683fa Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 31 Jul 2025 23:13:49 -0700 Subject: [PATCH 05/21] remove trait cleverness --- .../resource/msg__traits.hpp.em | 61 +++++++++++++------ .../test/rosidl_generator_cpp/test_traits.cpp | 7 ++- .../include/rosidl_runtime_cpp/traits.hpp | 11 ---- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/rosidl_generator_cpp/resource/msg__traits.hpp.em b/rosidl_generator_cpp/resource/msg__traits.hpp.em index 34e431cd1..db203d389 100644 --- a/rosidl_generator_cpp/resource/msg__traits.hpp.em +++ b/rosidl_generator_cpp/resource/msg__traits.hpp.em @@ -1,5 +1,6 @@ @# Included from rosidl_generator_cpp/resource/idl__traits.hpp.em @{ +from rosidl_generator_cpp import msg_type_to_cpp from rosidl_parser.definition import ACTION_FEEDBACK_SUFFIX from rosidl_parser.definition import ACTION_GOAL_SUFFIX from rosidl_parser.definition import ACTION_RESULT_SUFFIX @@ -13,6 +14,7 @@ from rosidl_parser.definition import EMPTY_STRUCTURE_REQUIRED_MEMBER_NAME from rosidl_parser.definition import NamespacedType from rosidl_parser.definition import AbstractSequence from rosidl_parser.definition import UnboundedSequence +from rosidl_parser.definition import OPTIONAL_ANNOTATION message_namespace = '::'.join(message.structure.namespaced_type.namespaces) message_typename = '::'.join(message.structure.namespaced_type.namespaced_name()) @@ -90,27 +92,41 @@ inline void to_flow_style_yaml( @[ end if]@ // member: @(member.name) - { + do { +@[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ + @(msg_type_to_cpp(member.type)) value; + if (msg.@(member.name) == std::nullopt) { + out << "@(member.name): null"; +@[ if i < len(message.structure.members) - 1]@ + out << ", "; +@[ end if]@ + break; + } else { + value = msg.@(member.name).value(); + } +@[ else]@ + auto value = msg.@(member.name); +@[ end if]@ @[ if isinstance(member.type, BasicType)]@ out << "@(member.name): "; @[ if member.type.typename in ('octet', 'char', 'wchar')]@ - rosidl_generator_traits::character_value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::character_value_to_yaml(value, out); @[ else]@ - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); @[ end if]@ @[ elif isinstance(member.type, AbstractGenericString)]@ out << "@(member.name): "; - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); @[ elif isinstance(member.type, NamespacedType)]@ out << "@(member.name): "; - to_flow_style_yaml(msg.@(member.name), out); + to_flow_style_yaml(value, out); @[ elif isinstance(member.type, (AbstractSequence, Array))]@ - if (msg.@(member.name).size() == 0) { + if (value.size() == 0) { out << "@(member.name): []"; } else { out << "@(member.name): ["; - size_t pending_items = msg.@(member.name).size(); - for (auto item : msg.@(member.name)) { + size_t pending_items = value.size(); + for (auto item : value) { @[ if isinstance(member.type.value_type, BasicType)]@ @[ if member.type.value_type.typename in ('octet', 'char', 'wchar')]@ rosidl_generator_traits::character_value_to_yaml(item, out); @@ -132,7 +148,7 @@ inline void to_flow_style_yaml( @[ if i < len(message.structure.members) - 1]@ out << ", "; @[ end if]@ - } + } while (false); @[ end for]@ out << "}"; @[end if]@ @@ -152,31 +168,42 @@ inline void to_block_style_yaml( @[ end if]@ // member: @(member.name) - { + do { if (indentation > 0) { out << std::string(indentation, ' '); } +@[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ + @(msg_type_to_cpp(member.type)) value; + if (msg.@(member.name) == std::nullopt) { + out << "@(member.name): null\n"; + break; + } else { + value = msg.@(member.name).value(); + } +@[ else]@ + auto value = msg.@(member.name); +@[ end if]@ @[ if isinstance(member.type, BasicType)]@ out << "@(member.name): "; @[ if member.type.typename in ('octet', 'char', 'wchar')]@ - rosidl_generator_traits::character_value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::character_value_to_yaml(value, out); @[ else]@ - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); @[ end if]@ out << "\n"; @[ elif isinstance(member.type, AbstractGenericString)]@ out << "@(member.name): "; - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); out << "\n"; @[ elif isinstance(member.type, NamespacedType)]@ out << "@(member.name):\n"; - to_block_style_yaml(msg.@(member.name), out, indentation + 2); + to_block_style_yaml(value, out, indentation + 2); @[ elif isinstance(member.type, (AbstractSequence, Array))]@ - if (msg.@(member.name).size() == 0) { + if (value.size() == 0) { out << "@(member.name): []\n"; } else { out << "@(member.name):\n"; - for (auto item : msg.@(member.name)) { + for (auto item : value) { if (indentation > 0) { out << std::string(indentation, ' '); } @@ -199,7 +226,7 @@ inline void to_block_style_yaml( } } @[ end if]@ - } + } while(false); @[ end for]@ @[end if]@ } // NOLINT(readability/fn_size) diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp index eefdbbc9e..32202dab0 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp @@ -255,12 +255,17 @@ alignment_check: 0 )", yaml.c_str()); } +} +TEST(Test_rosidl_generator_traits, optional_msgs) { { const rosidl_generator_tests::msg::OptionalIdl optional_msg; EXPECT_STREQ( - "optional_float: std::nullopt\ndefault_optional_float: 32.0000\n", + "optional_float: null\ndefault_optional_float: 32.0000\n", to_yaml(optional_msg).c_str()); + EXPECT_STREQ( + "{optional_float: null, default_optional_float: 32.0000}", + to_yaml(optional_msg, true).c_str()); } } diff --git a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp index f50785f7c..5a4773c66 100644 --- a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp +++ b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp @@ -20,7 +20,6 @@ #include #include #include -#include #include namespace rosidl_generator_traits @@ -151,16 +150,6 @@ inline void value_to_yaml(const std::u16string & value, std::ostream & out) out << "\""; } -template -inline void value_to_yaml(const std::optional value, std::ostream & out) -{ - if (value.has_value()) { - value_to_yaml(value.value(), out); - } else { - out << "std::nullopt"; - } -} - template inline const char * data_type(); From 9074fa6736e2c3e683ebce7774e82188af1c45bf Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 31 Jul 2025 23:34:55 -0700 Subject: [PATCH 06/21] simplify traits --- rosidl_generator_cpp/resource/msg__traits.hpp.em | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/rosidl_generator_cpp/resource/msg__traits.hpp.em b/rosidl_generator_cpp/resource/msg__traits.hpp.em index db203d389..d6436b1d1 100644 --- a/rosidl_generator_cpp/resource/msg__traits.hpp.em +++ b/rosidl_generator_cpp/resource/msg__traits.hpp.em @@ -94,15 +94,13 @@ inline void to_flow_style_yaml( // member: @(member.name) do { @[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ - @(msg_type_to_cpp(member.type)) value; + auto value = msg.@(member.name).value(); if (msg.@(member.name) == std::nullopt) { out << "@(member.name): null"; @[ if i < len(message.structure.members) - 1]@ out << ", "; @[ end if]@ break; - } else { - value = msg.@(member.name).value(); } @[ else]@ auto value = msg.@(member.name); @@ -173,12 +171,10 @@ inline void to_block_style_yaml( out << std::string(indentation, ' '); } @[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ - @(msg_type_to_cpp(member.type)) value; + auto value = msg.@(member.name).value(); if (msg.@(member.name) == std::nullopt) { out << "@(member.name): null\n"; break; - } else { - value = msg.@(member.name).value(); } @[ else]@ auto value = msg.@(member.name); From f267742493a2739ca0bb70cfc4d2dfc46a9883ca Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 1 Aug 2025 14:37:38 -0700 Subject: [PATCH 07/21] Add optional parsing Signed-off-by: Michael Carlstrom --- rosidl_adapter/rosidl_adapter/parser.py | 30 +++++++++++++++++-- .../rosidl_adapter/resource/struct.idl.em | 6 ++++ rosidl_adapter/test/data/action/Test.action | 3 ++ .../test/data/action/Test.expected.idl | 3 ++ .../test/data/msg/Test.expected.idl | 14 +++++++++ rosidl_adapter/test/data/msg/Test.msg | 7 +++++ .../test/data/srv/Test.expected.idl | 3 ++ rosidl_adapter/test/data/srv/Test.srv | 1 + rosidl_parser/test/msg/MyMessage.idl | 4 +++ rosidl_parser/test/test_parser.py | 8 ++++- 10 files changed, 75 insertions(+), 4 deletions(-) diff --git a/rosidl_adapter/rosidl_adapter/parser.py b/rosidl_adapter/rosidl_adapter/parser.py index aeee4b933..1d5e307e7 100644 --- a/rosidl_adapter/rosidl_adapter/parser.py +++ b/rosidl_adapter/rosidl_adapter/parser.py @@ -19,6 +19,8 @@ from typing import Final, Iterable, List, Optional, Tuple, TYPE_CHECKING, TypedDict, Union PACKAGE_NAME_MESSAGE_TYPE_SEPARATOR: Final = '/' +ANNOTATION_DELIMITER: Final = '@' +OPTIONAL_ANNOTATION: Final = ANNOTATION_DELIMITER + 'optional' COMMENT_DELIMITER: Final = '#' CONSTANT_SEPARATOR: Final = '=' ARRAY_UPPER_BOUND_TOKEN: Final = '<=' @@ -83,6 +85,7 @@ class Annotations(TypedDict, total=False): comment: List[str] unit: str + optional: bool class InvalidSpecification(Exception): @@ -109,6 +112,10 @@ class UnknownMessageType(InvalidSpecification): pass +class MultipleOptionalAnnotations(InvalidSpecification): + pass + + class InvalidValue(Exception): def __init__(self, type_: Union['Type', str], value_string: str, @@ -408,7 +415,7 @@ def __init__(self, pkg_name: str, msg_name: str, fields: Iterable['Field'], self.msg_name = msg_name self.annotations: 'Annotations' = {} - self.fields = [] + self.fields: list[Field] = [] for index, field in enumerate(fields): if not isinstance(field, Field): raise TypeError("field %u must be a 'Field' instance" % index) @@ -422,7 +429,7 @@ def __init__(self, pkg_name: str, msg_name: str, fields: Iterable['Field'], 'the fields iterable contains duplicate names: %s' % ', '.join(sorted(duplicate_field_names))) - self.constants = [] + self.constants: list[Constant] = [] for index, constant in enumerate(constants): if not isinstance(constant, Constant): raise TypeError("constant %u must be a 'Constant' instance" % @@ -485,10 +492,11 @@ def parse_message_string(pkg_name: str, msg_name: str, fields: List[Field] = [] constants: List[Constant] = [] last_element: Union[Field, Constant, None] = None # either a field or a constant + is_optional = False # replace tabs with spaces message_string = message_string.replace('\t', ' ') - current_comments = [] + current_comments: list[str] = [] message_comments, lines = extract_file_level_comments(message_string) for line in lines: line = line.rstrip() @@ -522,6 +530,19 @@ def parse_message_string(pkg_name: str, msg_name: str, if not line: continue + # TODO multiline optionals + annotation_index = line.rfind(OPTIONAL_ANNOTATION) + if annotation_index >= 0: + if is_optional: + raise MultipleOptionalAnnotations('Already declared @optional.' + f' Error detected with {line}.') + + line = line[len(OPTIONAL_ANNOTATION):].lstrip() + is_optional = True + + if not line: + continue + type_string, _, rest = line.partition(' ') rest = rest.lstrip() if not rest: @@ -555,6 +576,9 @@ def parse_message_string(pkg_name: str, msg_name: str, last_element = constants[-1] # add "unused" comments to the field / constant + if is_optional: + last_element.annotations['optional'] = is_optional + is_optional = False comment_lines = last_element.annotations.setdefault( 'comment', []) comment_lines += current_comments diff --git a/rosidl_adapter/rosidl_adapter/resource/struct.idl.em b/rosidl_adapter/rosidl_adapter/resource/struct.idl.em index 04d89f471..d91b2de8b 100644 --- a/rosidl_adapter/rosidl_adapter/resource/struct.idl.em +++ b/rosidl_adapter/rosidl_adapter/resource/struct.idl.em @@ -49,6 +49,9 @@ else: @[ end if]@ @[ end for]@ ) +@[ end if]@ +@[ if 'optional' in constant.annotations]@ + @@optional @[ end if]@ const @(get_idl_type(constant.type)) @(constant.name) = @(to_idl_literal(get_idl_type(constant.type), constant.value)); @[ end for]@ @@ -81,6 +84,9 @@ else: @[ end for]@ ) @[ end if]@ +@[ if 'optional' in field.annotations]@ + @@optional +@[ end if]@ @[ if field.default_value is not None]@ @@default (value=@(to_idl_literal(get_idl_type(field.type), field.default_value))) @[ end if]@ diff --git a/rosidl_adapter/test/data/action/Test.action b/rosidl_adapter/test/data/action/Test.action index b09834058..702167bfa 100644 --- a/rosidl_adapter/test/data/action/Test.action +++ b/rosidl_adapter/test/data/action/Test.action @@ -23,6 +23,9 @@ string string_value # ok docs bool ok + +@optional +bool optional_bool --- # feedback definition # more diff --git a/rosidl_adapter/test/data/action/Test.expected.idl b/rosidl_adapter/test/data/action/Test.expected.idl index 7732f944c..6a8249cec 100644 --- a/rosidl_adapter/test/data/action/Test.expected.idl +++ b/rosidl_adapter/test/data/action/Test.expected.idl @@ -50,6 +50,9 @@ module test_msgs { @verbatim (language="comment", text= "ok docs") boolean ok; + + @optional + boolean optional_bool; }; @verbatim (language="comment", text= "feedback definition" "\n" diff --git a/rosidl_adapter/test/data/msg/Test.expected.idl b/rosidl_adapter/test/data/msg/Test.expected.idl index a7bf20a0e..bcafa854c 100644 --- a/rosidl_adapter/test/data/msg/Test.expected.idl +++ b/rosidl_adapter/test/data/msg/Test.expected.idl @@ -5,6 +5,10 @@ module test_msgs { module msg { + module Test_Constants { + @optional + const float INLINE_CONSTANT = 32.0; + }; @verbatim (language="comment", text= "msg level doc") struct Test { @@ -40,6 +44,16 @@ module test_msgs { int64 int64_value; uint64 uint64_value; + + @optional + float multiline_optional; + + @optional + float inline_optional; + + @optional + @default (value=32.0) + float inline_default_optional; }; }; }; diff --git a/rosidl_adapter/test/data/msg/Test.msg b/rosidl_adapter/test/data/msg/Test.msg index 6b7c16236..8a6d284e0 100644 --- a/rosidl_adapter/test/data/msg/Test.msg +++ b/rosidl_adapter/test/data/msg/Test.msg @@ -15,3 +15,10 @@ int32 int32_value uint32 uint32_value int64 int64_value uint64 uint64_value + +@optional +float32 multiline_optional + +@optional float32 inline_optional +@optional float32 inline_default_optional 32.0 +@optional float32 INLINE_CONSTANT=32.0 diff --git a/rosidl_adapter/test/data/srv/Test.expected.idl b/rosidl_adapter/test/data/srv/Test.expected.idl index e2225a5d1..02cadceac 100644 --- a/rosidl_adapter/test/data/srv/Test.expected.idl +++ b/rosidl_adapter/test/data/srv/Test.expected.idl @@ -50,6 +50,9 @@ module test_msgs { @verbatim (language="comment", text= "8") boolean ok; + + @optional + boolean optional_bool; }; }; }; diff --git a/rosidl_adapter/test/data/srv/Test.srv b/rosidl_adapter/test/data/srv/Test.srv index 5486cba0d..2a24f2a58 100644 --- a/rosidl_adapter/test/data/srv/Test.srv +++ b/rosidl_adapter/test/data/srv/Test.srv @@ -23,3 +23,4 @@ string string_value # 8 bool ok +@optional bool optional_bool diff --git a/rosidl_parser/test/msg/MyMessage.idl b/rosidl_parser/test/msg/MyMessage.idl index f2d3cfa61..0348c546c 100644 --- a/rosidl_parser/test/msg/MyMessage.idl +++ b/rosidl_parser/test/msg/MyMessage.idl @@ -81,6 +81,10 @@ module rosidl_parser { float fixed_frac_only; @default ( value=7d ) float fixed_int_only; + + // Optional test + @optional + int32 optional_int; }; }; }; diff --git a/rosidl_parser/test/test_parser.py b/rosidl_parser/test/test_parser.py index 61b164742..e7a66f70c 100644 --- a/rosidl_parser/test/test_parser.py +++ b/rosidl_parser/test/test_parser.py @@ -134,7 +134,7 @@ def test_message_parser_structure(message_idl_file: IdlFile) -> None: 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) == 46 assert isinstance(structure.members[0].type, BasicType) assert structure.members[0].type.typename == 'int16' @@ -304,6 +304,12 @@ def test_message_parser_annotations(message_idl_file: IdlFile) -> None: assert len(structure.members[44].annotations) == 1 assert structure.members[44].annotations[0].name == 'default' + assert isinstance(structure.members[45].type, BasicType) + assert structure.members[45].type.typename == 'int32' + assert structure.members[45].name == 'optional_int' + assert len(structure.members[45].annotations) == 1 + assert structure.members[45].annotations[0].name == 'optional' + @pytest.fixture(scope='module') def service_idl_file() -> IdlFile: From df0ac5b06fe4d88a15851dae69167ca632145e4e Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 7 Aug 2025 10:57:51 -0700 Subject: [PATCH 08/21] Update rosidl_adapter/rosidl_adapter/parser.py Co-authored-by: William Woodall Signed-off-by: Michael Carlstrom --- rosidl_adapter/rosidl_adapter/parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rosidl_adapter/rosidl_adapter/parser.py b/rosidl_adapter/rosidl_adapter/parser.py index 1d5e307e7..ae5212f94 100644 --- a/rosidl_adapter/rosidl_adapter/parser.py +++ b/rosidl_adapter/rosidl_adapter/parser.py @@ -534,8 +534,8 @@ def parse_message_string(pkg_name: str, msg_name: str, annotation_index = line.rfind(OPTIONAL_ANNOTATION) if annotation_index >= 0: if is_optional: - raise MultipleOptionalAnnotations('Already declared @optional.' - f' Error detected with {line}.') + raise MultipleOptionalAnnotations( + f'Already declared @optional. Error detected with {line}.') line = line[len(OPTIONAL_ANNOTATION):].lstrip() is_optional = True From 882893393a2f75fdf6e9e70f386ff6027a4cc17f Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 7 Aug 2025 22:54:47 -0700 Subject: [PATCH 09/21] remove todo Signed-off-by: Michael Carlstrom --- rosidl_adapter/rosidl_adapter/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rosidl_adapter/rosidl_adapter/parser.py b/rosidl_adapter/rosidl_adapter/parser.py index ae5212f94..72ca02db8 100644 --- a/rosidl_adapter/rosidl_adapter/parser.py +++ b/rosidl_adapter/rosidl_adapter/parser.py @@ -530,7 +530,6 @@ def parse_message_string(pkg_name: str, msg_name: str, if not line: continue - # TODO multiline optionals annotation_index = line.rfind(OPTIONAL_ANNOTATION) if annotation_index >= 0: if is_optional: From d596f9710629e1ac29371f73fb49009e513a59ee Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 09:13:06 -0700 Subject: [PATCH 10/21] init Signed-off-by: Michael Carlstrom --- rosidl_parser/test/msg/MyMessage.idl | 4 ++++ rosidl_parser/test/test_parser.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/rosidl_parser/test/msg/MyMessage.idl b/rosidl_parser/test/msg/MyMessage.idl index f2d3cfa61..0348c546c 100644 --- a/rosidl_parser/test/msg/MyMessage.idl +++ b/rosidl_parser/test/msg/MyMessage.idl @@ -81,6 +81,10 @@ module rosidl_parser { float fixed_frac_only; @default ( value=7d ) float fixed_int_only; + + // Optional test + @optional + int32 optional_int; }; }; }; diff --git a/rosidl_parser/test/test_parser.py b/rosidl_parser/test/test_parser.py index 61b164742..e7a66f70c 100644 --- a/rosidl_parser/test/test_parser.py +++ b/rosidl_parser/test/test_parser.py @@ -134,7 +134,7 @@ def test_message_parser_structure(message_idl_file: IdlFile) -> None: 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) == 46 assert isinstance(structure.members[0].type, BasicType) assert structure.members[0].type.typename == 'int16' @@ -304,6 +304,12 @@ def test_message_parser_annotations(message_idl_file: IdlFile) -> None: assert len(structure.members[44].annotations) == 1 assert structure.members[44].annotations[0].name == 'default' + assert isinstance(structure.members[45].type, BasicType) + assert structure.members[45].type.typename == 'int32' + assert structure.members[45].name == 'optional_int' + assert len(structure.members[45].annotations) == 1 + assert structure.members[45].annotations[0].name == 'optional' + @pytest.fixture(scope='module') def service_idl_file() -> IdlFile: From 6db6c3f3b529e3d2a9f11b1a75bc9409db30d0bb Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 15:24:50 -0700 Subject: [PATCH 11/21] initial implementation Signed-off-by: Michael Carlstrom --- .../resource/idl__struct.hpp.em | 1 + .../resource/msg__struct.hpp.em | 5 ++- .../rosidl_generator_cpp/__init__.py | 38 ++++++++++++++----- rosidl_generator_tests/CMakeLists.txt | 1 + rosidl_generator_tests/msg/OptionalIdl.idl | 17 +++++++++ .../rosidl_generator_cpp/test_msg_builder.cpp | 15 ++++++++ .../test_msg_initialization.cpp | 10 +++++ .../test/rosidl_generator_cpp/test_traits.cpp | 8 ++++ rosidl_parser/rosidl_parser/definition.py | 2 + .../include/rosidl_runtime_cpp/traits.hpp | 11 ++++++ 10 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 rosidl_generator_tests/msg/OptionalIdl.idl diff --git a/rosidl_generator_cpp/resource/idl__struct.hpp.em b/rosidl_generator_cpp/resource/idl__struct.hpp.em index b8f985617..58e42a3ed 100644 --- a/rosidl_generator_cpp/resource/idl__struct.hpp.em +++ b/rosidl_generator_cpp/resource/idl__struct.hpp.em @@ -30,6 +30,7 @@ include_directives = set() #include #include #include +#include #include #include diff --git a/rosidl_generator_cpp/resource/msg__struct.hpp.em b/rosidl_generator_cpp/resource/msg__struct.hpp.em index 6e583888f..85349860c 100644 --- a/rosidl_generator_cpp/resource/msg__struct.hpp.em +++ b/rosidl_generator_cpp/resource/msg__struct.hpp.em @@ -4,6 +4,7 @@ from rosidl_generator_cpp import create_init_alloc_and_member_lists from rosidl_generator_cpp import escape_string from rosidl_generator_cpp import escape_wstring from rosidl_generator_cpp import msg_type_to_cpp +from rosidl_generator_cpp import member_to_cpp from rosidl_generator_cpp import MSG_TYPE_TO_CPP from rosidl_parser.definition import AbstractNestedType from rosidl_parser.definition import AbstractString @@ -247,7 +248,7 @@ non_defaulted_zero_initialized_members = [ // field types and members @[for member in message.structure.members]@ using _@(member.name)_type = - @(msg_type_to_cpp(member.type)); + @(member_to_cpp(member)); _@(member.name)_type @(member.name); @[end for]@ @@ -255,7 +256,7 @@ non_defaulted_zero_initialized_members = [ // setters for named parameter idiom @[ for member in message.structure.members]@ Type & set__@(member.name)( - const @(msg_type_to_cpp(member.type)) & _arg) + const @(member_to_cpp(member)) & _arg) { this->@(member.name) = _arg; return *this; diff --git a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py index 85db597c7..934392895 100644 --- a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py +++ b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py @@ -14,17 +14,22 @@ from ast import literal_eval from typing import List +from typing import Union from rosidl_parser.definition import AbstractGenericString from rosidl_parser.definition import AbstractNestedType from rosidl_parser.definition import AbstractSequence from rosidl_parser.definition import AbstractString +from rosidl_parser.definition import AbstractType 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 FLOATING_POINT_TYPES +from rosidl_parser.definition import Member +from rosidl_parser.definition import Message from rosidl_parser.definition import NamespacedType +from rosidl_parser.definition import OPTIONAL_ANNOTATION from rosidl_parser.definition import UnboundedSequence from rosidl_pycommon import generate_files @@ -76,7 +81,7 @@ def prefix_with_bom_if_necessary(content: str) -> str: } -def msg_type_only_to_cpp(type_): +def msg_type_only_to_cpp(type_: AbstractType) -> str: """ Convert a message type into the C++ declaration, ignoring array types. @@ -103,7 +108,7 @@ def msg_type_only_to_cpp(type_): return cpp_type -def msg_type_to_cpp(type_): +def msg_type_to_cpp(type_: AbstractType) -> str: """ Convert a message type into the C++ declaration, along with the array type. @@ -134,7 +139,16 @@ def msg_type_to_cpp(type_): return cpp_type -def value_to_cpp(type_, value): +def member_to_cpp(member: Member) -> str: + """Convert a member into the C++ declaration. This exists to add optional support.""" + cpp_type = msg_type_to_cpp(member.type) + + if member.has_annotation(OPTIONAL_ANNOTATION): + return f'std::optional<{cpp_type}>' + return cpp_type + + +def value_to_cpp(type_: AbstractType, value) -> str: """ Convert a python value into a string representing that value in C++. @@ -149,7 +163,9 @@ def value_to_cpp(type_, value): """ assert not isinstance(type_, NamespacedType), \ "Could not convert non-primitive type '%s' to CPP" % (type_) - assert value is not None, "Value for type '%s' must not be None" % (type_) + + if value is None: + return 'std::nullopt' if not isinstance(type_, AbstractNestedType): return primitive_value_to_cpp(type_, value) @@ -171,7 +187,7 @@ def value_to_cpp(type_, value): return cpp_value -def primitive_value_to_cpp(type_, value): +def primitive_value_to_cpp(type_: Union[BasicType, AbstractGenericString], value) -> str: """ Convert a python value into a string representing that value in C++. @@ -242,17 +258,17 @@ def default_value_from_type(type_): return 0 -def escape_string(s): +def escape_string(s: str) -> str: s = s.replace('\\', '\\\\') s = s.replace('"', '\\"') return s -def escape_wstring(s): +def escape_wstring(s: str) -> str: return escape_string(s) -def create_init_alloc_and_member_lists(message): +def create_init_alloc_and_member_lists(message: Message): # A Member object represents the information we need to know to initialize # a single member of the class. class Member: @@ -298,7 +314,11 @@ def add_member(self, member): for field in message.structure.members: member = Member(field.name) member.type = field.type - if isinstance(field.type, Array): + if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): + # Use existing logic for default + member.default_value = value_to_cpp(field.type, None) + + elif isinstance(field.type, Array): alloc_list.append(field.name + '(_alloc)') if isinstance(field.type.value_type, BasicType) or \ isinstance(field.type.value_type, AbstractGenericString): diff --git a/rosidl_generator_tests/CMakeLists.txt b/rosidl_generator_tests/CMakeLists.txt index bd858f870..8023bb00d 100644 --- a/rosidl_generator_tests/CMakeLists.txt +++ b/rosidl_generator_tests/CMakeLists.txt @@ -35,6 +35,7 @@ if(BUILD_TESTING) ${test_interface_files_MSG_FILES} ${test_interface_files_SRV_FILES} msg/BasicIdl.idl + msg/OptionalIdl.idl msg/SmallConstant.msg ADD_LINTER_TESTS SKIP_INSTALL diff --git a/rosidl_generator_tests/msg/OptionalIdl.idl b/rosidl_generator_tests/msg/OptionalIdl.idl new file mode 100644 index 000000000..a7cd23cf9 --- /dev/null +++ b/rosidl_generator_tests/msg/OptionalIdl.idl @@ -0,0 +1,17 @@ +module rosidl_generator_tests { + module msg { + struct OptionalIdl { + @optional float optional_float; + + @default ( value=32.0 ) + @optional + float default_optional_float; + + // @optional + // string optional_string; + + // @optional + // short optional_short_array[3]; + }; + }; +}; diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp index 229668c27..4dedc8a77 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_builder.cpp @@ -14,11 +14,14 @@ #include +#include + #include "rosidl_generator_tests/msg/detail/arrays__builder.hpp" #include "rosidl_generator_tests/msg/detail/basic_types__builder.hpp" #include "rosidl_generator_tests/msg/detail/empty__builder.hpp" #include "rosidl_generator_tests/msg/detail/multi_nested__builder.hpp" #include "rosidl_generator_tests/msg/detail/nested__builder.hpp" +#include "rosidl_generator_tests/msg/detail/optional_idl__builder.hpp" TEST(Test_msg_initialization, build) { ::rosidl_generator_tests::msg::BasicTypes basic = @@ -200,4 +203,16 @@ TEST(Test_msg_initialization, build) { rosidl_generator_tests::msg::Empty empty = rosidl_generator_tests::build(); ASSERT_EQ(0, empty.structure_needs_at_least_one_member); + + rosidl_generator_tests::msg::OptionalIdl optional = + rosidl_generator_tests::build() + .optional_float(0.1f) + .default_optional_float(2.0); + // .optional_short_array({{-100, -200, -100}}); + // .optional_string("init"); + + ASSERT_EQ(0.1f, optional.optional_float); + ASSERT_EQ(2.0, optional.default_optional_float); + // ASSERT_EQ({-100, -200, -100}, optional.default_optional_float); + // ASSERT_EQ("init", optional.optional_string) } diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp index 668aed0b0..3d4121143 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_msg_initialization.cpp @@ -17,9 +17,11 @@ #include #include +#include #include "rosidl_generator_tests/msg/defaults.hpp" #include "rosidl_generator_tests/msg/bounded_sequences.hpp" +#include "rosidl_generator_tests/msg/optional_idl.hpp" template struct ScopeExit @@ -171,6 +173,14 @@ TEST(Test_msg_initialization, defaults_only_constructor) { ASSERT_EQ(0LL, basic.int64_value); } +TEST(Test_msg_initialization, optional_msg_initialization) { + rosidl_generator_tests::msg::OptionalIdl optional_msg; + ASSERT_EQ(std::nullopt, optional_msg.optional_float); + ASSERT_EQ(32.0, optional_msg.default_optional_float); + // ASSERT_EQ(std::nullopt, optional_msg.optional_short_array); + // ASSERT_EQ(std::nullopt, optional_msg.optional_string); +} + // This is a test to ensure that when the user passes SKIP to the constructor, // it does no initialization. TEST(Test_msg_initialization, skip_constructor) { diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp index 7e16d450c..eefdbbc9e 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp @@ -22,6 +22,7 @@ #include "rosidl_generator_tests/msg/empty.hpp" #include "rosidl_generator_tests/msg/bounded_sequences.hpp" #include "rosidl_generator_tests/msg/nested.hpp" +#include "rosidl_generator_tests/msg/optional_idl.hpp" #include "rosidl_generator_tests/msg/strings.hpp" #include "rosidl_generator_tests/msg/w_strings.hpp" #include "rosidl_generator_tests/srv/empty.hpp" @@ -254,6 +255,13 @@ alignment_check: 0 )", yaml.c_str()); } + + { + const rosidl_generator_tests::msg::OptionalIdl optional_msg; + EXPECT_STREQ( + "optional_float: std::nullopt\ndefault_optional_float: 32.0000\n", + to_yaml(optional_msg).c_str()); + } } TEST(Test_rosidl_generator_traits, to_yaml_flow_style) { diff --git a/rosidl_parser/rosidl_parser/definition.py b/rosidl_parser/rosidl_parser/definition.py index 5873ba470..24036a94a 100644 --- a/rosidl_parser/rosidl_parser/definition.py +++ b/rosidl_parser/rosidl_parser/definition.py @@ -141,6 +141,8 @@ ACTION_RESULT_SERVICE_SUFFIX: Final = '_GetResult' ACTION_FEEDBACK_MESSAGE_SUFFIX: Final = '_FeedbackMessage' +OPTIONAL_ANNOTATION: Final = 'optional' + class AbstractType: """The abstract base class for all types.""" diff --git a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp index 5a4773c66..f50785f7c 100644 --- a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp +++ b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace rosidl_generator_traits @@ -150,6 +151,16 @@ inline void value_to_yaml(const std::u16string & value, std::ostream & out) out << "\""; } +template +inline void value_to_yaml(const std::optional value, std::ostream & out) +{ + if (value.has_value()) { + value_to_yaml(value.value(), out); + } else { + out << "std::nullopt"; + } +} + template inline const char * data_type(); From 62030399730c67d969a5f93292ed37bdcd661a66 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 16:13:21 -0700 Subject: [PATCH 12/21] explicit nullopt default Signed-off-by: Michael Carlstrom --- .../rosidl_generator_cpp/__init__.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py index 934392895..c3ebebb5d 100644 --- a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py +++ b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py @@ -14,6 +14,7 @@ from ast import literal_eval from typing import List +from typing import Optional from typing import Union from rosidl_parser.definition import AbstractGenericString @@ -273,15 +274,15 @@ def create_init_alloc_and_member_lists(message: Message): # a single member of the class. class Member: - def __init__(self, name): + def __init__(self, name: str): self.name = name - self.default_value = None - self.zero_value = None + self.default_value: Optional[str] = None + self.zero_value: Optional[str] = None self.zero_need_array_override = False - self.type = None + self.type: Optional[AbstractType] = None self.num_prealloc = 0 - def same_default_and_zero_value(self, other): + def same_default_and_zero_value(self, other) -> bool: return self.default_value == other.default_value and \ self.zero_value == other.zero_value @@ -292,10 +293,10 @@ def same_default_and_zero_value(self, other): # value). class CommonMemberSet: - def __init__(self): - self.members = [] + def __init__(self) -> None: + self.members: list[Member] = [] - def add_member(self, member): + def add_member(self, member: Member) -> bool: if not self.members or self.members[-1].same_default_and_zero_value(member): self.members.append(member) return True @@ -308,17 +309,14 @@ def add_member(self, member): # initializion in the allocator constructor # member_list - The list of members that we will generate initialization code # for in the body of the constructors - init_list = [] - alloc_list = [] - member_list = [] + init_list: list[str] = [] + alloc_list: list[str] = [] + member_list: list[CommonMemberSet] = [] for field in message.structure.members: member = Member(field.name) member.type = field.type - if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): - # Use existing logic for default - member.default_value = value_to_cpp(field.type, None) - elif isinstance(field.type, Array): + if isinstance(field.type, Array): alloc_list.append(field.name + '(_alloc)') if isinstance(field.type.value_type, BasicType) or \ isinstance(field.type.value_type, AbstractGenericString): @@ -362,4 +360,8 @@ def add_member(self, member): commonset.add_member(member) member_list.append(commonset) + # Override default if optional + if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): + member.default_value = value_to_cpp(field.type, None) + return init_list, alloc_list, member_list From 288cc1bb795dbcac7e4a5515bc3ff4b3f494322b Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 29 Jul 2025 18:23:16 -0700 Subject: [PATCH 13/21] switch default to zero_value Signed-off-by: Michael Carlstrom --- rosidl_generator_cpp/rosidl_generator_cpp/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py index c3ebebb5d..d4175555c 100644 --- a/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py +++ b/rosidl_generator_cpp/rosidl_generator_cpp/__init__.py @@ -360,8 +360,8 @@ def add_member(self, member: Member) -> bool: commonset.add_member(member) member_list.append(commonset) - # Override default if optional - if field.has_annotation(OPTIONAL_ANNOTATION) and not field.has_annotation('default'): - member.default_value = value_to_cpp(field.type, None) + # Override zero_value if optional + if field.has_annotation(OPTIONAL_ANNOTATION): + member.zero_value = value_to_cpp(field.type, None) return init_list, alloc_list, member_list From 66bff3c0520ec72ca2bb5488e0b2c7f26dae3780 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 31 Jul 2025 23:13:49 -0700 Subject: [PATCH 14/21] remove trait cleverness Signed-off-by: Michael Carlstrom --- .../resource/msg__traits.hpp.em | 61 +++++++++++++------ .../test/rosidl_generator_cpp/test_traits.cpp | 7 ++- .../include/rosidl_runtime_cpp/traits.hpp | 11 ---- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/rosidl_generator_cpp/resource/msg__traits.hpp.em b/rosidl_generator_cpp/resource/msg__traits.hpp.em index 34e431cd1..db203d389 100644 --- a/rosidl_generator_cpp/resource/msg__traits.hpp.em +++ b/rosidl_generator_cpp/resource/msg__traits.hpp.em @@ -1,5 +1,6 @@ @# Included from rosidl_generator_cpp/resource/idl__traits.hpp.em @{ +from rosidl_generator_cpp import msg_type_to_cpp from rosidl_parser.definition import ACTION_FEEDBACK_SUFFIX from rosidl_parser.definition import ACTION_GOAL_SUFFIX from rosidl_parser.definition import ACTION_RESULT_SUFFIX @@ -13,6 +14,7 @@ from rosidl_parser.definition import EMPTY_STRUCTURE_REQUIRED_MEMBER_NAME from rosidl_parser.definition import NamespacedType from rosidl_parser.definition import AbstractSequence from rosidl_parser.definition import UnboundedSequence +from rosidl_parser.definition import OPTIONAL_ANNOTATION message_namespace = '::'.join(message.structure.namespaced_type.namespaces) message_typename = '::'.join(message.structure.namespaced_type.namespaced_name()) @@ -90,27 +92,41 @@ inline void to_flow_style_yaml( @[ end if]@ // member: @(member.name) - { + do { +@[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ + @(msg_type_to_cpp(member.type)) value; + if (msg.@(member.name) == std::nullopt) { + out << "@(member.name): null"; +@[ if i < len(message.structure.members) - 1]@ + out << ", "; +@[ end if]@ + break; + } else { + value = msg.@(member.name).value(); + } +@[ else]@ + auto value = msg.@(member.name); +@[ end if]@ @[ if isinstance(member.type, BasicType)]@ out << "@(member.name): "; @[ if member.type.typename in ('octet', 'char', 'wchar')]@ - rosidl_generator_traits::character_value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::character_value_to_yaml(value, out); @[ else]@ - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); @[ end if]@ @[ elif isinstance(member.type, AbstractGenericString)]@ out << "@(member.name): "; - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); @[ elif isinstance(member.type, NamespacedType)]@ out << "@(member.name): "; - to_flow_style_yaml(msg.@(member.name), out); + to_flow_style_yaml(value, out); @[ elif isinstance(member.type, (AbstractSequence, Array))]@ - if (msg.@(member.name).size() == 0) { + if (value.size() == 0) { out << "@(member.name): []"; } else { out << "@(member.name): ["; - size_t pending_items = msg.@(member.name).size(); - for (auto item : msg.@(member.name)) { + size_t pending_items = value.size(); + for (auto item : value) { @[ if isinstance(member.type.value_type, BasicType)]@ @[ if member.type.value_type.typename in ('octet', 'char', 'wchar')]@ rosidl_generator_traits::character_value_to_yaml(item, out); @@ -132,7 +148,7 @@ inline void to_flow_style_yaml( @[ if i < len(message.structure.members) - 1]@ out << ", "; @[ end if]@ - } + } while (false); @[ end for]@ out << "}"; @[end if]@ @@ -152,31 +168,42 @@ inline void to_block_style_yaml( @[ end if]@ // member: @(member.name) - { + do { if (indentation > 0) { out << std::string(indentation, ' '); } +@[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ + @(msg_type_to_cpp(member.type)) value; + if (msg.@(member.name) == std::nullopt) { + out << "@(member.name): null\n"; + break; + } else { + value = msg.@(member.name).value(); + } +@[ else]@ + auto value = msg.@(member.name); +@[ end if]@ @[ if isinstance(member.type, BasicType)]@ out << "@(member.name): "; @[ if member.type.typename in ('octet', 'char', 'wchar')]@ - rosidl_generator_traits::character_value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::character_value_to_yaml(value, out); @[ else]@ - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); @[ end if]@ out << "\n"; @[ elif isinstance(member.type, AbstractGenericString)]@ out << "@(member.name): "; - rosidl_generator_traits::value_to_yaml(msg.@(member.name), out); + rosidl_generator_traits::value_to_yaml(value, out); out << "\n"; @[ elif isinstance(member.type, NamespacedType)]@ out << "@(member.name):\n"; - to_block_style_yaml(msg.@(member.name), out, indentation + 2); + to_block_style_yaml(value, out, indentation + 2); @[ elif isinstance(member.type, (AbstractSequence, Array))]@ - if (msg.@(member.name).size() == 0) { + if (value.size() == 0) { out << "@(member.name): []\n"; } else { out << "@(member.name):\n"; - for (auto item : msg.@(member.name)) { + for (auto item : value) { if (indentation > 0) { out << std::string(indentation, ' '); } @@ -199,7 +226,7 @@ inline void to_block_style_yaml( } } @[ end if]@ - } + } while(false); @[ end for]@ @[end if]@ } // NOLINT(readability/fn_size) diff --git a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp index eefdbbc9e..32202dab0 100644 --- a/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp +++ b/rosidl_generator_tests/test/rosidl_generator_cpp/test_traits.cpp @@ -255,12 +255,17 @@ alignment_check: 0 )", yaml.c_str()); } +} +TEST(Test_rosidl_generator_traits, optional_msgs) { { const rosidl_generator_tests::msg::OptionalIdl optional_msg; EXPECT_STREQ( - "optional_float: std::nullopt\ndefault_optional_float: 32.0000\n", + "optional_float: null\ndefault_optional_float: 32.0000\n", to_yaml(optional_msg).c_str()); + EXPECT_STREQ( + "{optional_float: null, default_optional_float: 32.0000}", + to_yaml(optional_msg, true).c_str()); } } diff --git a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp index f50785f7c..5a4773c66 100644 --- a/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp +++ b/rosidl_runtime_cpp/include/rosidl_runtime_cpp/traits.hpp @@ -20,7 +20,6 @@ #include #include #include -#include #include namespace rosidl_generator_traits @@ -151,16 +150,6 @@ inline void value_to_yaml(const std::u16string & value, std::ostream & out) out << "\""; } -template -inline void value_to_yaml(const std::optional value, std::ostream & out) -{ - if (value.has_value()) { - value_to_yaml(value.value(), out); - } else { - out << "std::nullopt"; - } -} - template inline const char * data_type(); From 3b4cd0eef376f869907cc644a5bfcfdeb51da6a6 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 31 Jul 2025 23:34:55 -0700 Subject: [PATCH 15/21] simplify traits Signed-off-by: Michael Carlstrom --- rosidl_generator_cpp/resource/msg__traits.hpp.em | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/rosidl_generator_cpp/resource/msg__traits.hpp.em b/rosidl_generator_cpp/resource/msg__traits.hpp.em index db203d389..d6436b1d1 100644 --- a/rosidl_generator_cpp/resource/msg__traits.hpp.em +++ b/rosidl_generator_cpp/resource/msg__traits.hpp.em @@ -94,15 +94,13 @@ inline void to_flow_style_yaml( // member: @(member.name) do { @[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ - @(msg_type_to_cpp(member.type)) value; + auto value = msg.@(member.name).value(); if (msg.@(member.name) == std::nullopt) { out << "@(member.name): null"; @[ if i < len(message.structure.members) - 1]@ out << ", "; @[ end if]@ break; - } else { - value = msg.@(member.name).value(); } @[ else]@ auto value = msg.@(member.name); @@ -173,12 +171,10 @@ inline void to_block_style_yaml( out << std::string(indentation, ' '); } @[ if member.has_annotation(OPTIONAL_ANNOTATION)]@ - @(msg_type_to_cpp(member.type)) value; + auto value = msg.@(member.name).value(); if (msg.@(member.name) == std::nullopt) { out << "@(member.name): null\n"; break; - } else { - value = msg.@(member.name).value(); } @[ else]@ auto value = msg.@(member.name); From 789c7508e784bafd5d0ab089cc82179fe73059b6 Mon Sep 17 00:00:00 2001 From: Chris Lalancette Date: Tue, 29 Jul 2025 20:36:12 +0000 Subject: [PATCH 16/21] Changelog. Signed-off-by: Chris Lalancette Signed-off-by: Michael Carlstrom --- rosidl_adapter/CHANGELOG.rst | 5 +++++ rosidl_cli/CHANGELOG.rst | 5 +++++ rosidl_cmake/CHANGELOG.rst | 3 +++ rosidl_generator_c/CHANGELOG.rst | 5 +++++ rosidl_generator_cpp/CHANGELOG.rst | 5 +++++ rosidl_generator_tests/CHANGELOG.rst | 3 +++ rosidl_generator_type_description/CHANGELOG.rst | 5 +++++ rosidl_parser/CHANGELOG.rst | 3 +++ rosidl_pycommon/CHANGELOG.rst | 5 +++++ rosidl_runtime_c/CHANGELOG.rst | 3 +++ rosidl_runtime_cpp/CHANGELOG.rst | 3 +++ rosidl_typesupport_interface/CHANGELOG.rst | 3 +++ rosidl_typesupport_introspection_c/CHANGELOG.rst | 5 +++++ rosidl_typesupport_introspection_cpp/CHANGELOG.rst | 5 +++++ rosidl_typesupport_introspection_tests/CHANGELOG.rst | 3 +++ 15 files changed, 61 insertions(+) diff --git a/rosidl_adapter/CHANGELOG.rst b/rosidl_adapter/CHANGELOG.rst index dbfe2cc96..8e31deb1e 100644 --- a/rosidl_adapter/CHANGELOG.rst +++ b/rosidl_adapter/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_adapter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* Uniform cmake minVersion (`#849 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_cli/CHANGELOG.rst b/rosidl_cli/CHANGELOG.rst index 3a8d7b144..81368063c 100644 --- a/rosidl_cli/CHANGELOG.rst +++ b/rosidl_cli/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_cli ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* fix setuptools deprecations (`#877 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ * rosidl_cli: Add type description support (`#857 `_) diff --git a/rosidl_cmake/CHANGELOG.rst b/rosidl_cmake/CHANGELOG.rst index 130878403..60c5ed5fb 100644 --- a/rosidl_cmake/CHANGELOG.rst +++ b/rosidl_cmake/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rosidl_cmake ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 5.0.0 (2025-07-01) ------------------ * fix cmake <3.10 deprecation (`#875 `_) diff --git a/rosidl_generator_c/CHANGELOG.rst b/rosidl_generator_c/CHANGELOG.rst index 6246dddaf..60c0e741e 100644 --- a/rosidl_generator_c/CHANGELOG.rst +++ b/rosidl_generator_c/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_generator_c ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* Uniform cmake minVersion (`#849 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ * rosidl_cli: Add type description support (`#857 `_) diff --git a/rosidl_generator_cpp/CHANGELOG.rst b/rosidl_generator_cpp/CHANGELOG.rst index fbdd0ae69..98a336711 100644 --- a/rosidl_generator_cpp/CHANGELOG.rst +++ b/rosidl_generator_cpp/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_generator_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* Uniform cmake minVersion (`#849 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ * rosidl_cli: Add type description support (`#857 `_) diff --git a/rosidl_generator_tests/CHANGELOG.rst b/rosidl_generator_tests/CHANGELOG.rst index 6ebec1ff6..16360dd96 100644 --- a/rosidl_generator_tests/CHANGELOG.rst +++ b/rosidl_generator_tests/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rosidl_generator_tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 5.0.0 (2025-07-01) ------------------ * fix cmake <3.10 deprecation (`#875 `_) diff --git a/rosidl_generator_type_description/CHANGELOG.rst b/rosidl_generator_type_description/CHANGELOG.rst index 3ce7b5c89..5af42c18b 100644 --- a/rosidl_generator_type_description/CHANGELOG.rst +++ b/rosidl_generator_type_description/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_generator_type_description ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* Uniform cmake minVersion (`#849 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ * rosidl_cli: Add type description support (`#857 `_) diff --git a/rosidl_parser/CHANGELOG.rst b/rosidl_parser/CHANGELOG.rst index 0b859c884..28e2afd2e 100644 --- a/rosidl_parser/CHANGELOG.rst +++ b/rosidl_parser/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rosidl_parser ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 5.0.0 (2025-07-01) ------------------ * fix cmake <3.10 deprecation (`#875 `_) diff --git a/rosidl_pycommon/CHANGELOG.rst b/rosidl_pycommon/CHANGELOG.rst index 0a3966ff4..845674229 100644 --- a/rosidl_pycommon/CHANGELOG.rst +++ b/rosidl_pycommon/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_pycommon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* fix setuptools deprecation (`#880 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_runtime_c/CHANGELOG.rst b/rosidl_runtime_c/CHANGELOG.rst index af51969d6..9d1a7a9a9 100644 --- a/rosidl_runtime_c/CHANGELOG.rst +++ b/rosidl_runtime_c/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rosidl_runtime_c ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 5.0.0 (2025-07-01) ------------------ * fix cmake <3.10 deprecation (`#875 `_) diff --git a/rosidl_runtime_cpp/CHANGELOG.rst b/rosidl_runtime_cpp/CHANGELOG.rst index 04cf592ab..9b1aba4ae 100644 --- a/rosidl_runtime_cpp/CHANGELOG.rst +++ b/rosidl_runtime_cpp/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rosidl_runtime_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 5.0.0 (2025-07-01) ------------------ * fix cmake <3.10 deprecation (`#875 `_) diff --git a/rosidl_typesupport_interface/CHANGELOG.rst b/rosidl_typesupport_interface/CHANGELOG.rst index f4834c77e..e8e54f2ec 100644 --- a/rosidl_typesupport_interface/CHANGELOG.rst +++ b/rosidl_typesupport_interface/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rosidl_typesupport_interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 5.0.0 (2025-07-01) ------------------ * fix cmake <3.10 deprecation (`#875 `_) diff --git a/rosidl_typesupport_introspection_c/CHANGELOG.rst b/rosidl_typesupport_introspection_c/CHANGELOG.rst index 6b0a5a9a8..9cb3b544a 100644 --- a/rosidl_typesupport_introspection_c/CHANGELOG.rst +++ b/rosidl_typesupport_introspection_c/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_typesupport_introspection_c ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* Uniform cmake minVersion (`#849 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ * rosidl_cli: Add type description support (`#857 `_) diff --git a/rosidl_typesupport_introspection_cpp/CHANGELOG.rst b/rosidl_typesupport_introspection_cpp/CHANGELOG.rst index 4b5dd0e4e..cad8314dc 100644 --- a/rosidl_typesupport_introspection_cpp/CHANGELOG.rst +++ b/rosidl_typesupport_introspection_cpp/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog for package rosidl_typesupport_introspection_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* Uniform cmake minVersion (`#849 `_) +* Contributors: mosfet80 + 5.0.0 (2025-07-01) ------------------ * rosidl_cli: Add type description support (`#857 `_) diff --git a/rosidl_typesupport_introspection_tests/CHANGELOG.rst b/rosidl_typesupport_introspection_tests/CHANGELOG.rst index 71009a10b..a4d0a7d6d 100644 --- a/rosidl_typesupport_introspection_tests/CHANGELOG.rst +++ b/rosidl_typesupport_introspection_tests/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package rosidl_typesupport_introspection_tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 5.0.0 (2025-07-01) ------------------ * fix cmake <3.10 deprecation (`#875 `_) From 87fa8045279c6312e21185607733adb9cf1463b9 Mon Sep 17 00:00:00 2001 From: Chris Lalancette Date: Tue, 29 Jul 2025 20:36:18 +0000 Subject: [PATCH 17/21] 5.0.1 Signed-off-by: Michael Carlstrom --- rosidl_adapter/CHANGELOG.rst | 4 ++-- rosidl_adapter/package.xml | 2 +- rosidl_cli/CHANGELOG.rst | 4 ++-- rosidl_cli/package.xml | 2 +- rosidl_cli/setup.py | 2 +- rosidl_cmake/CHANGELOG.rst | 4 ++-- rosidl_cmake/package.xml | 2 +- rosidl_generator_c/CHANGELOG.rst | 4 ++-- rosidl_generator_c/package.xml | 2 +- rosidl_generator_cpp/CHANGELOG.rst | 4 ++-- rosidl_generator_cpp/package.xml | 2 +- rosidl_generator_tests/CHANGELOG.rst | 4 ++-- rosidl_generator_tests/package.xml | 2 +- rosidl_generator_type_description/CHANGELOG.rst | 4 ++-- rosidl_generator_type_description/package.xml | 2 +- rosidl_parser/CHANGELOG.rst | 4 ++-- rosidl_parser/package.xml | 2 +- rosidl_pycommon/CHANGELOG.rst | 4 ++-- rosidl_pycommon/package.xml | 2 +- rosidl_pycommon/setup.py | 2 +- rosidl_runtime_c/CHANGELOG.rst | 4 ++-- rosidl_runtime_c/package.xml | 2 +- rosidl_runtime_cpp/CHANGELOG.rst | 4 ++-- rosidl_runtime_cpp/package.xml | 2 +- rosidl_typesupport_interface/CHANGELOG.rst | 4 ++-- rosidl_typesupport_interface/package.xml | 2 +- rosidl_typesupport_introspection_c/CHANGELOG.rst | 4 ++-- rosidl_typesupport_introspection_c/package.xml | 2 +- rosidl_typesupport_introspection_cpp/CHANGELOG.rst | 4 ++-- rosidl_typesupport_introspection_cpp/package.xml | 2 +- rosidl_typesupport_introspection_tests/CHANGELOG.rst | 4 ++-- rosidl_typesupport_introspection_tests/package.xml | 2 +- 32 files changed, 47 insertions(+), 47 deletions(-) diff --git a/rosidl_adapter/CHANGELOG.rst b/rosidl_adapter/CHANGELOG.rst index 8e31deb1e..1046a9064 100644 --- a/rosidl_adapter/CHANGELOG.rst +++ b/rosidl_adapter/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_adapter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * Uniform cmake minVersion (`#849 `_) * Contributors: mosfet80 diff --git a/rosidl_adapter/package.xml b/rosidl_adapter/package.xml index 4770b97b7..944769387 100644 --- a/rosidl_adapter/package.xml +++ b/rosidl_adapter/package.xml @@ -2,7 +2,7 @@ rosidl_adapter - 5.0.0 + 5.0.1 API and scripts to parse .msg/.srv/.action files and convert them to .idl. diff --git a/rosidl_cli/CHANGELOG.rst b/rosidl_cli/CHANGELOG.rst index 81368063c..9f0dadc90 100644 --- a/rosidl_cli/CHANGELOG.rst +++ b/rosidl_cli/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_cli ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * fix setuptools deprecations (`#877 `_) * Contributors: mosfet80 diff --git a/rosidl_cli/package.xml b/rosidl_cli/package.xml index 5f182e73e..a0235fe3d 100644 --- a/rosidl_cli/package.xml +++ b/rosidl_cli/package.xml @@ -2,7 +2,7 @@ rosidl_cli - 5.0.0 + 5.0.1 Command line tools for ROS interface generation. diff --git a/rosidl_cli/setup.py b/rosidl_cli/setup.py index d9fd082a3..c6bb44df5 100644 --- a/rosidl_cli/setup.py +++ b/rosidl_cli/setup.py @@ -3,7 +3,7 @@ setup( name='rosidl_cli', - version='5.0.0', + version='5.0.1', packages=find_packages(exclude=['test']), extras_require={ 'completion': ['argcomplete'], diff --git a/rosidl_cmake/CHANGELOG.rst b/rosidl_cmake/CHANGELOG.rst index 60c5ed5fb..df057690f 100644 --- a/rosidl_cmake/CHANGELOG.rst +++ b/rosidl_cmake/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_cmake ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_cmake/package.xml b/rosidl_cmake/package.xml index 419bf1a1d..c46752ed6 100644 --- a/rosidl_cmake/package.xml +++ b/rosidl_cmake/package.xml @@ -2,7 +2,7 @@ rosidl_cmake - 5.0.0 + 5.0.1 The CMake functionality to invoke code generation for ROS interface files. Aditya Pande diff --git a/rosidl_generator_c/CHANGELOG.rst b/rosidl_generator_c/CHANGELOG.rst index 60c0e741e..adeeeb1d5 100644 --- a/rosidl_generator_c/CHANGELOG.rst +++ b/rosidl_generator_c/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_generator_c ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * Uniform cmake minVersion (`#849 `_) * Contributors: mosfet80 diff --git a/rosidl_generator_c/package.xml b/rosidl_generator_c/package.xml index 4f7c4beb6..521c80aa4 100644 --- a/rosidl_generator_c/package.xml +++ b/rosidl_generator_c/package.xml @@ -2,7 +2,7 @@ rosidl_generator_c - 5.0.0 + 5.0.1 Generate the ROS interfaces in C. Aditya Pande diff --git a/rosidl_generator_cpp/CHANGELOG.rst b/rosidl_generator_cpp/CHANGELOG.rst index 98a336711..9fa4e5ae3 100644 --- a/rosidl_generator_cpp/CHANGELOG.rst +++ b/rosidl_generator_cpp/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_generator_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * Uniform cmake minVersion (`#849 `_) * Contributors: mosfet80 diff --git a/rosidl_generator_cpp/package.xml b/rosidl_generator_cpp/package.xml index d8c40fdd1..f139b2990 100644 --- a/rosidl_generator_cpp/package.xml +++ b/rosidl_generator_cpp/package.xml @@ -2,7 +2,7 @@ rosidl_generator_cpp - 5.0.0 + 5.0.1 Generate the ROS interfaces in C++. Aditya Pande diff --git a/rosidl_generator_tests/CHANGELOG.rst b/rosidl_generator_tests/CHANGELOG.rst index 16360dd96..5e28b9f67 100644 --- a/rosidl_generator_tests/CHANGELOG.rst +++ b/rosidl_generator_tests/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_generator_tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_generator_tests/package.xml b/rosidl_generator_tests/package.xml index b8f8ef6dd..557e5ad03 100644 --- a/rosidl_generator_tests/package.xml +++ b/rosidl_generator_tests/package.xml @@ -2,7 +2,7 @@ rosidl_generator_tests - 5.0.0 + 5.0.1 Integration tests for rosidl_generator_c and rosidl_generator_cpp packages. Aditya Pande diff --git a/rosidl_generator_type_description/CHANGELOG.rst b/rosidl_generator_type_description/CHANGELOG.rst index 5af42c18b..066ff4cdf 100644 --- a/rosidl_generator_type_description/CHANGELOG.rst +++ b/rosidl_generator_type_description/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_generator_type_description ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * Uniform cmake minVersion (`#849 `_) * Contributors: mosfet80 diff --git a/rosidl_generator_type_description/package.xml b/rosidl_generator_type_description/package.xml index 8e479ac3f..35570b815 100644 --- a/rosidl_generator_type_description/package.xml +++ b/rosidl_generator_type_description/package.xml @@ -2,7 +2,7 @@ rosidl_generator_type_description - 5.0.0 + 5.0.1 Generate hashes and descriptions of ROS 2 interface types, per REP-2011. Emerson Knapp diff --git a/rosidl_parser/CHANGELOG.rst b/rosidl_parser/CHANGELOG.rst index 28e2afd2e..c30b3952f 100644 --- a/rosidl_parser/CHANGELOG.rst +++ b/rosidl_parser/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_parser ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_parser/package.xml b/rosidl_parser/package.xml index bdda08eaa..23f94fbba 100644 --- a/rosidl_parser/package.xml +++ b/rosidl_parser/package.xml @@ -2,7 +2,7 @@ rosidl_parser - 5.0.0 + 5.0.1 The parser for `.idl` ROS interface files. Aditya Pande diff --git a/rosidl_pycommon/CHANGELOG.rst b/rosidl_pycommon/CHANGELOG.rst index 845674229..df4dbcafc 100644 --- a/rosidl_pycommon/CHANGELOG.rst +++ b/rosidl_pycommon/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_pycommon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * fix setuptools deprecation (`#880 `_) * Contributors: mosfet80 diff --git a/rosidl_pycommon/package.xml b/rosidl_pycommon/package.xml index 4187aeb4f..a63cac347 100644 --- a/rosidl_pycommon/package.xml +++ b/rosidl_pycommon/package.xml @@ -2,7 +2,7 @@ rosidl_pycommon - 5.0.0 + 5.0.1 Common Python functions used by rosidl packages. Aditya Pande diff --git a/rosidl_pycommon/setup.py b/rosidl_pycommon/setup.py index 3fe95ea7c..61eb71ae7 100644 --- a/rosidl_pycommon/setup.py +++ b/rosidl_pycommon/setup.py @@ -5,7 +5,7 @@ setup( name=package_name, - version='5.0.0', + version='5.0.1', packages=find_packages(exclude=['test']), data_files=[ ('share/ament_index/resource_index/packages', diff --git a/rosidl_runtime_c/CHANGELOG.rst b/rosidl_runtime_c/CHANGELOG.rst index 9d1a7a9a9..57ba612ae 100644 --- a/rosidl_runtime_c/CHANGELOG.rst +++ b/rosidl_runtime_c/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_runtime_c ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_runtime_c/package.xml b/rosidl_runtime_c/package.xml index a451017e5..c308b4629 100644 --- a/rosidl_runtime_c/package.xml +++ b/rosidl_runtime_c/package.xml @@ -2,7 +2,7 @@ rosidl_runtime_c - 5.0.0 + 5.0.1 Provides definitions, initialization and finalization functions, and macros for getting and working with rosidl typesupport types in C. Aditya Pande diff --git a/rosidl_runtime_cpp/CHANGELOG.rst b/rosidl_runtime_cpp/CHANGELOG.rst index 9b1aba4ae..d4a1a7c7a 100644 --- a/rosidl_runtime_cpp/CHANGELOG.rst +++ b/rosidl_runtime_cpp/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_runtime_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_runtime_cpp/package.xml b/rosidl_runtime_cpp/package.xml index 9e3647bac..5a5123a82 100644 --- a/rosidl_runtime_cpp/package.xml +++ b/rosidl_runtime_cpp/package.xml @@ -2,7 +2,7 @@ rosidl_runtime_cpp - 5.0.0 + 5.0.1 Provides definitions and templated functions for getting and working with rosidl typesupport types in C++. Aditya Pande diff --git a/rosidl_typesupport_interface/CHANGELOG.rst b/rosidl_typesupport_interface/CHANGELOG.rst index e8e54f2ec..8d9128d96 100644 --- a/rosidl_typesupport_interface/CHANGELOG.rst +++ b/rosidl_typesupport_interface/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_typesupport_interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_typesupport_interface/package.xml b/rosidl_typesupport_interface/package.xml index cf12a4303..4a93791e3 100644 --- a/rosidl_typesupport_interface/package.xml +++ b/rosidl_typesupport_interface/package.xml @@ -2,7 +2,7 @@ rosidl_typesupport_interface - 5.0.0 + 5.0.1 The interface for rosidl typesupport packages. diff --git a/rosidl_typesupport_introspection_c/CHANGELOG.rst b/rosidl_typesupport_introspection_c/CHANGELOG.rst index 9cb3b544a..399a11331 100644 --- a/rosidl_typesupport_introspection_c/CHANGELOG.rst +++ b/rosidl_typesupport_introspection_c/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_typesupport_introspection_c ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * Uniform cmake minVersion (`#849 `_) * Contributors: mosfet80 diff --git a/rosidl_typesupport_introspection_c/package.xml b/rosidl_typesupport_introspection_c/package.xml index 129744116..44770d3d5 100644 --- a/rosidl_typesupport_introspection_c/package.xml +++ b/rosidl_typesupport_introspection_c/package.xml @@ -2,7 +2,7 @@ rosidl_typesupport_introspection_c - 5.0.0 + 5.0.1 Generate the message type support for dynamic message construction in C. diff --git a/rosidl_typesupport_introspection_cpp/CHANGELOG.rst b/rosidl_typesupport_introspection_cpp/CHANGELOG.rst index cad8314dc..a8ad55f6e 100644 --- a/rosidl_typesupport_introspection_cpp/CHANGELOG.rst +++ b/rosidl_typesupport_introspection_cpp/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_typesupport_introspection_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ * Uniform cmake minVersion (`#849 `_) * Contributors: mosfet80 diff --git a/rosidl_typesupport_introspection_cpp/package.xml b/rosidl_typesupport_introspection_cpp/package.xml index 73e5542a4..c41e30ba9 100644 --- a/rosidl_typesupport_introspection_cpp/package.xml +++ b/rosidl_typesupport_introspection_cpp/package.xml @@ -2,7 +2,7 @@ rosidl_typesupport_introspection_cpp - 5.0.0 + 5.0.1 Generate the message type support for dynamic message construction in C++. diff --git a/rosidl_typesupport_introspection_tests/CHANGELOG.rst b/rosidl_typesupport_introspection_tests/CHANGELOG.rst index a4d0a7d6d..8fff6d17b 100644 --- a/rosidl_typesupport_introspection_tests/CHANGELOG.rst +++ b/rosidl_typesupport_introspection_tests/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package rosidl_typesupport_introspection_tests ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +5.0.1 (2025-07-29) +------------------ 5.0.0 (2025-07-01) ------------------ diff --git a/rosidl_typesupport_introspection_tests/package.xml b/rosidl_typesupport_introspection_tests/package.xml index 21ab5fe6a..1c551c2b3 100644 --- a/rosidl_typesupport_introspection_tests/package.xml +++ b/rosidl_typesupport_introspection_tests/package.xml @@ -2,7 +2,7 @@ rosidl_typesupport_introspection_tests - 5.0.0 + 5.0.1 Integration tests of the rosidl_typesupport_introspection_c/cpp packages. Aditya Pande From 5d65e3dd90c236bbd44d89f4febfd74c8ffc616e Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 1 Aug 2025 14:37:38 -0700 Subject: [PATCH 18/21] Add optional parsing Signed-off-by: Michael Carlstrom --- rosidl_adapter/rosidl_adapter/parser.py | 30 +++++++++++++++++-- .../rosidl_adapter/resource/struct.idl.em | 6 ++++ rosidl_adapter/test/data/action/Test.action | 3 ++ .../test/data/action/Test.expected.idl | 3 ++ .../test/data/msg/Test.expected.idl | 14 +++++++++ rosidl_adapter/test/data/msg/Test.msg | 7 +++++ .../test/data/srv/Test.expected.idl | 3 ++ rosidl_adapter/test/data/srv/Test.srv | 1 + 8 files changed, 64 insertions(+), 3 deletions(-) diff --git a/rosidl_adapter/rosidl_adapter/parser.py b/rosidl_adapter/rosidl_adapter/parser.py index aeee4b933..1d5e307e7 100644 --- a/rosidl_adapter/rosidl_adapter/parser.py +++ b/rosidl_adapter/rosidl_adapter/parser.py @@ -19,6 +19,8 @@ from typing import Final, Iterable, List, Optional, Tuple, TYPE_CHECKING, TypedDict, Union PACKAGE_NAME_MESSAGE_TYPE_SEPARATOR: Final = '/' +ANNOTATION_DELIMITER: Final = '@' +OPTIONAL_ANNOTATION: Final = ANNOTATION_DELIMITER + 'optional' COMMENT_DELIMITER: Final = '#' CONSTANT_SEPARATOR: Final = '=' ARRAY_UPPER_BOUND_TOKEN: Final = '<=' @@ -83,6 +85,7 @@ class Annotations(TypedDict, total=False): comment: List[str] unit: str + optional: bool class InvalidSpecification(Exception): @@ -109,6 +112,10 @@ class UnknownMessageType(InvalidSpecification): pass +class MultipleOptionalAnnotations(InvalidSpecification): + pass + + class InvalidValue(Exception): def __init__(self, type_: Union['Type', str], value_string: str, @@ -408,7 +415,7 @@ def __init__(self, pkg_name: str, msg_name: str, fields: Iterable['Field'], self.msg_name = msg_name self.annotations: 'Annotations' = {} - self.fields = [] + self.fields: list[Field] = [] for index, field in enumerate(fields): if not isinstance(field, Field): raise TypeError("field %u must be a 'Field' instance" % index) @@ -422,7 +429,7 @@ def __init__(self, pkg_name: str, msg_name: str, fields: Iterable['Field'], 'the fields iterable contains duplicate names: %s' % ', '.join(sorted(duplicate_field_names))) - self.constants = [] + self.constants: list[Constant] = [] for index, constant in enumerate(constants): if not isinstance(constant, Constant): raise TypeError("constant %u must be a 'Constant' instance" % @@ -485,10 +492,11 @@ def parse_message_string(pkg_name: str, msg_name: str, fields: List[Field] = [] constants: List[Constant] = [] last_element: Union[Field, Constant, None] = None # either a field or a constant + is_optional = False # replace tabs with spaces message_string = message_string.replace('\t', ' ') - current_comments = [] + current_comments: list[str] = [] message_comments, lines = extract_file_level_comments(message_string) for line in lines: line = line.rstrip() @@ -522,6 +530,19 @@ def parse_message_string(pkg_name: str, msg_name: str, if not line: continue + # TODO multiline optionals + annotation_index = line.rfind(OPTIONAL_ANNOTATION) + if annotation_index >= 0: + if is_optional: + raise MultipleOptionalAnnotations('Already declared @optional.' + f' Error detected with {line}.') + + line = line[len(OPTIONAL_ANNOTATION):].lstrip() + is_optional = True + + if not line: + continue + type_string, _, rest = line.partition(' ') rest = rest.lstrip() if not rest: @@ -555,6 +576,9 @@ def parse_message_string(pkg_name: str, msg_name: str, last_element = constants[-1] # add "unused" comments to the field / constant + if is_optional: + last_element.annotations['optional'] = is_optional + is_optional = False comment_lines = last_element.annotations.setdefault( 'comment', []) comment_lines += current_comments diff --git a/rosidl_adapter/rosidl_adapter/resource/struct.idl.em b/rosidl_adapter/rosidl_adapter/resource/struct.idl.em index 04d89f471..d91b2de8b 100644 --- a/rosidl_adapter/rosidl_adapter/resource/struct.idl.em +++ b/rosidl_adapter/rosidl_adapter/resource/struct.idl.em @@ -49,6 +49,9 @@ else: @[ end if]@ @[ end for]@ ) +@[ end if]@ +@[ if 'optional' in constant.annotations]@ + @@optional @[ end if]@ const @(get_idl_type(constant.type)) @(constant.name) = @(to_idl_literal(get_idl_type(constant.type), constant.value)); @[ end for]@ @@ -81,6 +84,9 @@ else: @[ end for]@ ) @[ end if]@ +@[ if 'optional' in field.annotations]@ + @@optional +@[ end if]@ @[ if field.default_value is not None]@ @@default (value=@(to_idl_literal(get_idl_type(field.type), field.default_value))) @[ end if]@ diff --git a/rosidl_adapter/test/data/action/Test.action b/rosidl_adapter/test/data/action/Test.action index b09834058..702167bfa 100644 --- a/rosidl_adapter/test/data/action/Test.action +++ b/rosidl_adapter/test/data/action/Test.action @@ -23,6 +23,9 @@ string string_value # ok docs bool ok + +@optional +bool optional_bool --- # feedback definition # more diff --git a/rosidl_adapter/test/data/action/Test.expected.idl b/rosidl_adapter/test/data/action/Test.expected.idl index 7732f944c..6a8249cec 100644 --- a/rosidl_adapter/test/data/action/Test.expected.idl +++ b/rosidl_adapter/test/data/action/Test.expected.idl @@ -50,6 +50,9 @@ module test_msgs { @verbatim (language="comment", text= "ok docs") boolean ok; + + @optional + boolean optional_bool; }; @verbatim (language="comment", text= "feedback definition" "\n" diff --git a/rosidl_adapter/test/data/msg/Test.expected.idl b/rosidl_adapter/test/data/msg/Test.expected.idl index a7bf20a0e..bcafa854c 100644 --- a/rosidl_adapter/test/data/msg/Test.expected.idl +++ b/rosidl_adapter/test/data/msg/Test.expected.idl @@ -5,6 +5,10 @@ module test_msgs { module msg { + module Test_Constants { + @optional + const float INLINE_CONSTANT = 32.0; + }; @verbatim (language="comment", text= "msg level doc") struct Test { @@ -40,6 +44,16 @@ module test_msgs { int64 int64_value; uint64 uint64_value; + + @optional + float multiline_optional; + + @optional + float inline_optional; + + @optional + @default (value=32.0) + float inline_default_optional; }; }; }; diff --git a/rosidl_adapter/test/data/msg/Test.msg b/rosidl_adapter/test/data/msg/Test.msg index 6b7c16236..8a6d284e0 100644 --- a/rosidl_adapter/test/data/msg/Test.msg +++ b/rosidl_adapter/test/data/msg/Test.msg @@ -15,3 +15,10 @@ int32 int32_value uint32 uint32_value int64 int64_value uint64 uint64_value + +@optional +float32 multiline_optional + +@optional float32 inline_optional +@optional float32 inline_default_optional 32.0 +@optional float32 INLINE_CONSTANT=32.0 diff --git a/rosidl_adapter/test/data/srv/Test.expected.idl b/rosidl_adapter/test/data/srv/Test.expected.idl index e2225a5d1..02cadceac 100644 --- a/rosidl_adapter/test/data/srv/Test.expected.idl +++ b/rosidl_adapter/test/data/srv/Test.expected.idl @@ -50,6 +50,9 @@ module test_msgs { @verbatim (language="comment", text= "8") boolean ok; + + @optional + boolean optional_bool; }; }; }; diff --git a/rosidl_adapter/test/data/srv/Test.srv b/rosidl_adapter/test/data/srv/Test.srv index 5486cba0d..2a24f2a58 100644 --- a/rosidl_adapter/test/data/srv/Test.srv +++ b/rosidl_adapter/test/data/srv/Test.srv @@ -23,3 +23,4 @@ string string_value # 8 bool ok +@optional bool optional_bool From 3969e3a05117da8ef2536ca57eb2619b3ace0a32 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 7 Aug 2025 10:57:51 -0700 Subject: [PATCH 19/21] Update rosidl_adapter/rosidl_adapter/parser.py Co-authored-by: William Woodall Signed-off-by: Michael Carlstrom --- rosidl_adapter/rosidl_adapter/parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rosidl_adapter/rosidl_adapter/parser.py b/rosidl_adapter/rosidl_adapter/parser.py index 1d5e307e7..ae5212f94 100644 --- a/rosidl_adapter/rosidl_adapter/parser.py +++ b/rosidl_adapter/rosidl_adapter/parser.py @@ -534,8 +534,8 @@ def parse_message_string(pkg_name: str, msg_name: str, annotation_index = line.rfind(OPTIONAL_ANNOTATION) if annotation_index >= 0: if is_optional: - raise MultipleOptionalAnnotations('Already declared @optional.' - f' Error detected with {line}.') + raise MultipleOptionalAnnotations( + f'Already declared @optional. Error detected with {line}.') line = line[len(OPTIONAL_ANNOTATION):].lstrip() is_optional = True From bfa7f0ef6c8717f9ec937320464408fb18d45ee5 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 7 Aug 2025 22:54:47 -0700 Subject: [PATCH 20/21] remove todo Signed-off-by: Michael Carlstrom --- rosidl_adapter/rosidl_adapter/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rosidl_adapter/rosidl_adapter/parser.py b/rosidl_adapter/rosidl_adapter/parser.py index ae5212f94..72ca02db8 100644 --- a/rosidl_adapter/rosidl_adapter/parser.py +++ b/rosidl_adapter/rosidl_adapter/parser.py @@ -530,7 +530,6 @@ def parse_message_string(pkg_name: str, msg_name: str, if not line: continue - # TODO multiline optionals annotation_index = line.rfind(OPTIONAL_ANNOTATION) if annotation_index >= 0: if is_optional: From 1724ebec71c4d364daac33b3cef0ea2458ce6b2e Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 22 Aug 2025 21:58:53 -0700 Subject: [PATCH 21/21] switch to msg file Signed-off-by: Michael Carlstrom --- rosidl_generator_tests/CMakeLists.txt | 2 +- rosidl_generator_tests/msg/Optional.msg | 11 +++++++++++ rosidl_generator_tests/msg/OptionalIdl.idl | 17 ----------------- 3 files changed, 12 insertions(+), 18 deletions(-) create mode 100644 rosidl_generator_tests/msg/Optional.msg delete mode 100644 rosidl_generator_tests/msg/OptionalIdl.idl diff --git a/rosidl_generator_tests/CMakeLists.txt b/rosidl_generator_tests/CMakeLists.txt index 8023bb00d..01f0d571d 100644 --- a/rosidl_generator_tests/CMakeLists.txt +++ b/rosidl_generator_tests/CMakeLists.txt @@ -35,7 +35,7 @@ if(BUILD_TESTING) ${test_interface_files_MSG_FILES} ${test_interface_files_SRV_FILES} msg/BasicIdl.idl - msg/OptionalIdl.idl + msg/Optional.msg msg/SmallConstant.msg ADD_LINTER_TESTS SKIP_INSTALL diff --git a/rosidl_generator_tests/msg/Optional.msg b/rosidl_generator_tests/msg/Optional.msg new file mode 100644 index 000000000..adec5452a --- /dev/null +++ b/rosidl_generator_tests/msg/Optional.msg @@ -0,0 +1,11 @@ +@optional +float32 optional_float + +@optional +float32 default_optional_float 32.0 + +# @optional +# string optional_string + +# @optional +# short optional_short_array[3] \ No newline at end of file diff --git a/rosidl_generator_tests/msg/OptionalIdl.idl b/rosidl_generator_tests/msg/OptionalIdl.idl deleted file mode 100644 index a7cd23cf9..000000000 --- a/rosidl_generator_tests/msg/OptionalIdl.idl +++ /dev/null @@ -1,17 +0,0 @@ -module rosidl_generator_tests { - module msg { - struct OptionalIdl { - @optional float optional_float; - - @default ( value=32.0 ) - @optional - float default_optional_float; - - // @optional - // string optional_string; - - // @optional - // short optional_short_array[3]; - }; - }; -};