diff --git a/rclcpp/include/rclcpp/dynamic_typesupport/type_description_conversions.hpp b/rclcpp/include/rclcpp/dynamic_typesupport/type_description_conversions.hpp new file mode 100644 index 0000000000..432d638303 --- /dev/null +++ b/rclcpp/include/rclcpp/dynamic_typesupport/type_description_conversions.hpp @@ -0,0 +1,215 @@ +// Copyright 2025 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__TYPE_DESCRIPTION_CONVERSIONS_HPP_ +#define RCLCPP__DYNAMIC_TYPESUPPORT__TYPE_DESCRIPTION_CONVERSIONS_HPP_ + +#include "rosidl_runtime_cpp/type_description/individual_type_description__struct.hpp" +#include "rosidl_runtime_cpp/type_description/type_description__struct.hpp" +#include "rosidl_runtime_cpp/type_description/type_source__struct.hpp" +#include "type_description_interfaces/msg/individual_type_description.hpp" +#include "type_description_interfaces/msg/type_description.hpp" +#include "type_description_interfaces/msg/type_source.hpp" + +namespace rclcpp +{ +namespace dynamic_typesupport +{ + +// IndividualTypeDescription ======================================================================= + +/// Convert a runtime individual type description struct to its corresponding message. +/** + * This function converts a rosidl_runtime_cpp::type_description::IndividualTypeDescription + * struct to the corresponding type_description_interfaces/msg/IndividualTypeDescription + * msg. + * + * \param[in] runtime_individual_description the runtime struct to convert + * \return the converted individual type description msg + */ +template< + typename ToAllocatorT = std::allocator, + typename FromAllocatorT +> +type_description_interfaces::msg::IndividualTypeDescription_ +convert_individual_type_description_runtime_to_msg( + const rosidl_runtime_cpp::type_description::IndividualTypeDescription_ & + runtime_individual_description, const ToAllocatorT & alloc = ToAllocatorT()) +{ + type_description_interfaces::msg::IndividualTypeDescription_ out(alloc); + out.type_name = runtime_individual_description.type_name; + + for (const auto & field : runtime_individual_description.fields) { + out.fields.emplace_back(); + out.fields.back().name = field.name; + out.fields.back().default_value = field.default_value; + + out.fields.back().type.type_id = field.type.type_id; + out.fields.back().type.capacity = field.type.capacity; + out.fields.back().type.string_capacity = field.type.string_capacity; + out.fields.back().type.nested_type_name = field.type.nested_type_name; + } + return out; +} + +/// Convert a individual type description message to its corresponding runtime struct. +/** + * This function converts a type_description_interfaces/msg/IndividualTypeDescription msg + * to the corresponding rosidl_runtime_cpp::type_description::IndividualTypeDescription + * struct. + * + * \param[in] description_msg the message to convert + * \return the converted runtime struct + */ +template< + typename ToAllocatorT = std::allocator, + typename FromAllocatorT +> +rosidl_runtime_cpp::type_description::IndividualTypeDescription_ +convert_individual_type_description_msg_to_runtime( + const type_description_interfaces::msg::IndividualTypeDescription_ & + individual_description_msg, const ToAllocatorT & alloc = ToAllocatorT()) +{ + rosidl_runtime_cpp::type_description::IndividualTypeDescription_ out(alloc); + out.type_name = individual_description_msg.type_name; + + for (const auto & field : individual_description_msg.fields) { + out.fields.emplace_back(); + out.fields.back().name = field.name; + out.fields.back().default_value = field.default_value; + + out.fields.back().type.type_id = field.type.type_id; + out.fields.back().type.capacity = field.type.capacity; + out.fields.back().type.string_capacity = field.type.string_capacity; + out.fields.back().type.nested_type_name = field.type.nested_type_name; + } + return out; +} + +// TypeDescription ================================================================================= + +/// Convert a runtime type description struct to its corresponding message. +/** + * This function converts a rosidl_runtime_cpp::type_description::TypeDescription + * struct to the corresponding type_description_interfaces/msg/TypeDescription + * msg. + * + * \param[in] runtime_description the runtime struct to convert + * \return the converted message + */ +template< + typename ToAllocatorT = std::allocator, + typename FromAllocatorT +> +type_description_interfaces::msg::TypeDescription_ +convert_type_description_runtime_to_msg( + const rosidl_runtime_cpp::type_description::TypeDescription_ & runtime_description, + const ToAllocatorT & alloc = ToAllocatorT()) +{ + type_description_interfaces::msg::TypeDescription_ out(alloc); + out.type_description = + convert_individual_type_description_runtime_to_msg(runtime_description.type_description, alloc); + + for (const auto & referenced_type_description : + runtime_description.referenced_type_descriptions) + { + out.referenced_type_descriptions.push_back(convert_individual_type_description_runtime_to_msg( + referenced_type_description, alloc)); + } + return out; +} + +/// Convert a type description message to its corresponding runtime struct. +/** + * This function converts a type_description_interfaces/msg/TypeDescription msg + * to the corresponding rosidl_runtime_cpp::type_description::TypeDescription + * struct. + * + * \param[in] description_msg the message to convert + * \return the converted runtime struct + */ +template< + typename ToAllocatorT = std::allocator, + typename FromAllocatorT +> +rosidl_runtime_cpp::type_description::TypeDescription_ +convert_type_description_msg_to_runtime( + const type_description_interfaces::msg::TypeDescription_ & description_msg, + const ToAllocatorT & alloc = ToAllocatorT()) +{ + rosidl_runtime_cpp::type_description::TypeDescription_ out(alloc); + out.type_description = + convert_individual_type_description_msg_to_runtime(description_msg.type_description, alloc); + + for (const auto & referenced_type_description : description_msg.referenced_type_descriptions) { + out.referenced_type_descriptions.push_back(convert_individual_type_description_msg_to_runtime( + referenced_type_description, alloc)); + } + return out; +} + +// TypeSource ====================================================================================== + +/// Convert a runtime type source struct to its corresponding message. +/** + * This function converts a rosidl_runtime_cpp::type_description::TypeSource + * struct to the corresponding type_description_interfaces/msg/TypeSource msg. + * + * \param[in] runtime_type_source the runtime struct to convert + * \return the converted message + */ +template< + typename ToAllocatorT = std::allocator, + typename FromAllocatorT +> +type_description_interfaces::msg::TypeSource_ +convert_type_source_sequence_runtime_to_msg( + const rosidl_runtime_cpp::type_description::TypeSource_ & runtime_type_source, + const ToAllocatorT & alloc = ToAllocatorT()) +{ + type_description_interfaces::msg::TypeSource_ out(alloc); + out.type_name = runtime_type_source.type_name; + out.encoding = runtime_type_source.encoding; + out.raw_file_contents = runtime_type_source.raw_file_contents; + return out; +} + +/// Convert a type source message to its corresponding runtime struct. +/** + * This function converts a type_description_interfaces/msg/TypeSource msg to + * the corresponding rosidl_runtime_cpp::type_description::TypeSource struct. + * + * \param[in] type_source_msg the message to convert + * \return the converted runtime struct + */ +template< + typename ToAllocatorT = std::allocator, + typename FromAllocatorT +> +rosidl_runtime_cpp::type_description::TypeSource_ +convert_type_source_sequence_msg_to_runtime( + const type_description_interfaces::msg::TypeSource_ & type_source_msg, + const ToAllocatorT & alloc = ToAllocatorT()) +{ + rosidl_runtime_cpp::type_description::TypeSource_ out(alloc); + out.type_name = type_source_msg.type_name; + out.encoding = type_source_msg.encoding; + out.raw_file_contents = type_source_msg.raw_file_contents; + return out; +} + +} // namespace dynamic_typesupport +} // namespace rclcpp + +#endif // RCLCPP__DYNAMIC_TYPESUPPORT__TYPE_DESCRIPTION_CONVERSIONS_HPP_ diff --git a/rclcpp/test/rclcpp/CMakeLists.txt b/rclcpp/test/rclcpp/CMakeLists.txt index 35fa8fdd32..38df39a252 100644 --- a/rclcpp/test/rclcpp/CMakeLists.txt +++ b/rclcpp/test/rclcpp/CMakeLists.txt @@ -28,6 +28,13 @@ ament_add_gtest( if(TARGET test_allocator_deleter) target_link_libraries(test_allocator_deleter ${PROJECT_NAME}) endif() + +ament_add_gtest(test_type_description_conversions + dynamic_typesupport/test_type_description_conversions.cpp) +if(TARGET test_type_description_conversions) + target_link_libraries(test_type_description_conversions ${PROJECT_NAME} ${rcl_interfaces_TARGETS}) +endif() + ament_add_gtest( test_exceptions exceptions/test_exceptions.cpp) diff --git a/rclcpp/test/rclcpp/dynamic_typesupport/test_type_description_conversions.cpp b/rclcpp/test/rclcpp/dynamic_typesupport/test_type_description_conversions.cpp new file mode 100644 index 0000000000..a83af8133d --- /dev/null +++ b/rclcpp/test/rclcpp/dynamic_typesupport/test_type_description_conversions.cpp @@ -0,0 +1,118 @@ +// Copyright 2025 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "rclcpp/dynamic_typesupport/type_description_conversions.hpp" +#include "rosidl_runtime_cpp/type_description/individual_type_description__struct.hpp" +#include "rosidl_runtime_cpp/type_description/type_description__struct.hpp" +#include "rosidl_runtime_cpp/type_description/type_source__struct.hpp" +#include "type_description_interfaces/msg/individual_type_description.hpp" +#include "type_description_interfaces/msg/type_description.hpp" +#include "type_description_interfaces/msg/type_source.hpp" + +using rclcpp::dynamic_typesupport:: +convert_individual_type_description_msg_to_runtime; +using rclcpp::dynamic_typesupport:: +convert_individual_type_description_runtime_to_msg; +using rclcpp::dynamic_typesupport::convert_type_description_msg_to_runtime; +using rclcpp::dynamic_typesupport::convert_type_description_runtime_to_msg; +using rclcpp::dynamic_typesupport::convert_type_source_sequence_msg_to_runtime; +using rclcpp::dynamic_typesupport::convert_type_source_sequence_runtime_to_msg; + +TEST(TestTypeDescriptionConversions, individual_type_description_roundtrip) +{ + rosidl_runtime_cpp::type_description::IndividualTypeDescription original; + original.type_name = "test_type_name"; + + original.fields.emplace_back(); + original.fields.back().name = "field1"; + original.fields.back().type.type_id = 1; + + original.fields.emplace_back(); + original.fields.back().name = "field2"; + original.fields.back().type.type_id = 2; + + type_description_interfaces::msg::IndividualTypeDescription msg = + convert_individual_type_description_runtime_to_msg(original); + rosidl_runtime_cpp::type_description::IndividualTypeDescription converted_back + = + convert_individual_type_description_msg_to_runtime(msg); + + EXPECT_EQ(original.type_name, converted_back.type_name); + ASSERT_EQ(original.fields.size(), converted_back.fields.size()); + for (size_t i = 0; i < original.fields.size(); ++i) { + EXPECT_EQ(original.fields[i].name, converted_back.fields[i].name); + EXPECT_EQ(original.fields[i].type, converted_back.fields[i].type); + } +} + +TEST(TestTypeDescriptionConversions, type_description_roundtrip) +{ + rosidl_runtime_cpp::type_description::TypeDescription original; + original.type_description.type_name = "main_type"; + original.type_description.fields.emplace_back(); + original.type_description.fields.back().name = "field1"; + original.type_description.fields.back().type.type_id = 1; + original.type_description.fields.emplace_back(); + original.type_description.fields.back().name = "field2"; + original.type_description.fields.back().type.type_id = 2; + + rosidl_runtime_cpp::type_description::IndividualTypeDescription ref1; + ref1.type_name = "ref_type_1"; + ref1.fields.emplace_back(); + ref1.fields.back().name = "ref_field1"; + ref1.fields.back().type.type_id = 1; + + rosidl_runtime_cpp::type_description::IndividualTypeDescription ref2; + ref2.type_name = "ref_type_2"; + ref2.fields.emplace_back(); + ref2.fields.back().name = "ref_field2"; + ref2.fields.back().type.type_id = 2; + + original.referenced_type_descriptions.push_back(ref1); + original.referenced_type_descriptions.push_back(ref2); + + type_description_interfaces::msg::TypeDescription msg = + convert_type_description_runtime_to_msg(original); + rosidl_runtime_cpp::type_description::TypeDescription converted_back = + convert_type_description_msg_to_runtime(msg); + + EXPECT_EQ(original.type_description.type_name, + converted_back.type_description.type_name); + ASSERT_EQ(original.referenced_type_descriptions.size(), + converted_back.referenced_type_descriptions.size()); + + for (size_t i = 0; i < original.referenced_type_descriptions.size(); ++i) { + EXPECT_EQ(original.referenced_type_descriptions[i].type_name, + converted_back.referenced_type_descriptions[i].type_name); + } +} + +TEST(TestTypeSourceConversions, type_source_roundtrip) +{ + rosidl_runtime_cpp::type_description::TypeSource original; + original.type_name = "test_type_name"; + original.encoding = "test_encoding"; + original.raw_file_contents = "test_contents"; + + type_description_interfaces::msg::TypeSource msg = + convert_type_source_sequence_runtime_to_msg(original); + rosidl_runtime_cpp::type_description::TypeSource converted_back = + convert_type_source_sequence_msg_to_runtime(msg); + + EXPECT_EQ(original.type_name, converted_back.type_name); + EXPECT_EQ(original.encoding, converted_back.encoding); + EXPECT_EQ(original.raw_file_contents, converted_back.raw_file_contents); +}