diff --git a/dynmsg/include/dynmsg/msg_parser.hpp b/dynmsg/include/dynmsg/msg_parser.hpp index 1bd9464..b9d6d10 100644 --- a/dynmsg/include/dynmsg/msg_parser.hpp +++ b/dynmsg/include/dynmsg/msg_parser.hpp @@ -34,6 +34,8 @@ namespace c * representation for the given ROS message. * * It is an error for the YAML representation to contain a field that is not in the ROS message. + * A std::runtime_error exception will be thrown in this case. + * * It is not an error for a field of the ROS message to not be specified in the YAML * representation; that field will be left uninitialised. */ diff --git a/dynmsg/src/msg_parser_c.cpp b/dynmsg/src/msg_parser_c.cpp index 6fdafc3..89bfa2b 100644 --- a/dynmsg/src/msg_parser_c.cpp +++ b/dynmsg/src/msg_parser_c.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "rosidl_runtime_c/string.h" #include "rosidl_runtime_c/string_functions.h" @@ -413,12 +414,23 @@ void yaml_to_rosmsg_impl( const TypeInfo * typeinfo, uint8_t * buffer) { + // Put all the YAML node keys into a set. We'll be removing the keys from the set as they are parsed. If at the end + // there are some keys left in the set, it means the YAML has extra fields that don't belong to the message, and an + // exception will be thrown. + std::unordered_set yaml_keys; + for (const auto& elem : root) + { + yaml_keys.insert(elem.first.as()); + } + + for (uint32_t i = 0; i < typeinfo->member_count_; i++) { const auto & member = typeinfo->members_[i]; if (!root[member.name_]) { continue; } + yaml_keys.erase(member.name_); switch (member.type_id_) { case rosidl_typesupport_introspection_c__ROS_TYPE_FLOAT: @@ -480,6 +492,16 @@ void yaml_to_rosmsg_impl( throw std::runtime_error("unknown type"); } } + if (!yaml_keys.empty()) { + std::stringstream error_message; + error_message << "Found unknown fields in the YAML not corresponding to a " << typeinfo->message_namespace_ << "/" + << typeinfo->message_name_ << " message:"; + for (const std::string& key : yaml_keys) + { + error_message << " " << key << std::endl; + } + throw std::runtime_error(error_message.str()); + } } } // namespace impl diff --git a/dynmsg/src/msg_parser_cpp.cpp b/dynmsg/src/msg_parser_cpp.cpp index 024751f..1574057 100644 --- a/dynmsg/src/msg_parser_cpp.cpp +++ b/dynmsg/src/msg_parser_cpp.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "rosidl_runtime_c/string.h" #include "rosidl_runtime_c/string_functions.h" @@ -502,6 +503,15 @@ void yaml_to_rosmsg_impl( const TypeInfo_Cpp * typeinfo, uint8_t * buffer) { + // Put all the YAML node keys into a set. We'll be removing the keys from the set as they are + // parsed. If at the end there are some keys left in the set, it means the YAML has extra fields + // that don't belong to the message, and an exception will be thrown. + std::unordered_set yaml_keys; + for (const auto& elem : root) + { + yaml_keys.insert(elem.first.as()); + } + DYNMSG_DEBUG(std::cout << "DEBUG: yaml_to_rosmsg_impl" << std::endl); DYNMSG_DEBUG( std::cout << "DEBUG: type_info message_namespace_: " << @@ -514,6 +524,7 @@ void yaml_to_rosmsg_impl( if (!root[member.name_]) { continue; } + yaml_keys.erase(member.name_); switch (member.type_id_) { case rosidl_typesupport_introspection_cpp::ROS_TYPE_FLOAT: @@ -575,6 +586,16 @@ void yaml_to_rosmsg_impl( throw std::runtime_error("unknown type"); } } + if (!yaml_keys.empty()) { + std::stringstream error_message; + error_message << "Found unknown fields in the YAML not corresponding to a " << typeinfo->message_namespace_ << "/" + << typeinfo->message_name_ << " message:"; + for (const std::string& key : yaml_keys) + { + error_message << " " << key << std::endl; + } + throw std::runtime_error(error_message.str()); + } } } // namespace impl diff --git a/dynmsg_demo/test/msg_parser_test.cpp b/dynmsg_demo/test/msg_parser_test.cpp index 3278386..ea57221 100644 --- a/dynmsg_demo/test/msg_parser_test.cpp +++ b/dynmsg_demo/test/msg_parser_test.cpp @@ -126,6 +126,16 @@ TEST(MsgParser, String) { dynmsg::c::ros_message_destroy(&generic_msg); } +TEST(MsgParser, StringInvalidFieldCpp) { + EXPECT_ANY_THROW(dynmsg::cpp::yaml_to_rosmsg( + InterfaceTypeName{"std_msgs", "String"}, "{ invalid_field: hello }")); +} + +TEST(MsgParser, StringInvalidFieldC) { + EXPECT_ANY_THROW(dynmsg::c::yaml_to_rosmsg( + InterfaceTypeName{"std_msgs", "String"}, "{ invalid_field: hello }")); +} + TEST(MsgParser, WideString) { auto generic_msg = dynmsg::c::yaml_to_rosmsg( InterfaceTypeName{"dynmsg_msgs", "WideString"},