Skip to content

Commit

Permalink
Type hash in interface codegen (rep2011) (#722)
Browse files Browse the repository at this point in the history
First part of type hash generation.  Adds in a new generator that
will generate the type hashes for every type, as well as supporting
structures for that.  This will all be written out to a generated file
at compile time.  Some additional details:

* Generate type hash during code gen
* Include referenced type descriptions
* Add type version hash to all 3 generators
* Output json
* rosidl generator creating and installing expected files
* Generate action struct
* Type hash depended upon and loaded by C and C++ generators
* Add rosidl_type_hash_t to rosidl_runtime_c
* Add type hash to introspection
* Parse type hash string utility
* Add schema checking test
* Generate hashes in C for services and actions

Signed-off-by: Emerson Knapp <[email protected]>
  • Loading branch information
emersonknapp authored Mar 15, 2023
1 parent 383ca18 commit b93c518
Show file tree
Hide file tree
Showing 36 changed files with 1,480 additions and 26 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ See [documentation](https://docs.ros.org/en/rolling/Concepts/About-Internal-Inte
* Generate the ROS interfaces in C
* [rosidl_generator_cpp](./rosidl_generator_cpp)
* Generate the ROS interfaces in C++
* [rosidl_generator_type_description](./rosidl_generator_type_desrciption)
* Generate SHA256 hash values and ROS 2 interface descriptions for use by other generators
* [rosidl_parser](./rosidl_parser)
* Parser for `.idl` ROS interface files
* [rosidl_runtime_c](./rosidl_runtime_c)
Expand Down
2 changes: 2 additions & 0 deletions rosidl_cmake/cmake/rosidl_write_generator_arguments.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ function(rosidl_write_generator_arguments output_file)
set(OPTIONAL_MULTI_VALUE_KEYWORDS
"ROS_INTERFACE_DEPENDENCIES" # since the dependencies can be empty
"TARGET_DEPENDENCIES"
"TYPE_HASH_TUPLES"
"INCLUDE_PATHS"
"ADDITIONAL_FILES")

cmake_parse_arguments(
Expand Down
1 change: 1 addition & 0 deletions rosidl_generator_c/cmake/register_c.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

macro(rosidl_generator_c_extras BIN GENERATOR_FILES TEMPLATE_DIR)
find_package(ament_cmake_core QUIET REQUIRED)
find_package(rosidl_generator_type_description QUIET REQUIRED)
ament_register_extension(
"rosidl_generate_idl_interfaces"
"rosidl_generator_c"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ rosidl_write_generator_arguments(
OUTPUT_DIR "${_output_path}"
TEMPLATE_DIR "${rosidl_generator_c_TEMPLATE_DIR}"
TARGET_DEPENDENCIES ${target_dependencies}
TYPE_HASH_TUPLES "${${rosidl_generate_interfaces_TARGET}__HASH_TUPLES}"
)

find_package(Python3 REQUIRED COMPONENTS Interpreter)
Expand Down Expand Up @@ -142,6 +143,9 @@ target_link_libraries(${rosidl_generate_interfaces_TARGET}${_target_suffix} PUBL
rosidl_runtime_c::rosidl_runtime_c
rosidl_typesupport_interface::rosidl_typesupport_interface
rcutils::rcutils)
add_dependencies(
${rosidl_generate_interfaces_TARGET}${_target_suffix}
${rosidl_generate_interfaces_TARGET}__rosidl_generator_type_description)

# Make top level generation target depend on this generated library
add_dependencies(
Expand Down
2 changes: 2 additions & 0 deletions rosidl_generator_c/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
<buildtool_export_depend>python3</buildtool_export_depend>
<buildtool_export_depend>rosidl_pycommon</buildtool_export_depend>

<build_export_depend>rosidl_generator_type_description</build_export_depend>
<build_export_depend>rosidl_typesupport_interface</build_export_depend>
<build_export_depend>rcutils</build_export_depend>

<exec_depend>ament_index_python</exec_depend>
<exec_depend>rosidl_cli</exec_depend>
<exec_depend>rosidl_parser</exec_depend>
<exec_depend>rcutils</exec_depend>
<exec_depend>rosidl_generator_type_description</exec_depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
Expand Down
66 changes: 52 additions & 14 deletions rosidl_generator_c/resource/idl__struct.h.em
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
@# - content (IdlContent, list of elements, e.g. Messages or Services)
@#######################################################################
@{
from rosidl_generator_c import idl_structure_type_to_c_typename
from rosidl_generator_c import type_hash_to_c_definition
from rosidl_pycommon import convert_camel_case_to_lower_case_underscore
include_parts = [package_name] + list(interface_path.parents[0].parts) + [
'detail', convert_camel_case_to_lower_case_underscore(interface_path.stem)]
Expand All @@ -32,6 +34,8 @@ extern "C"
#include <stddef.h>
#include <stdint.h>

#include "rosidl_runtime_c/type_hash.h"

@#######################################################################
@# Handle message
@#######################################################################
Expand All @@ -43,7 +47,7 @@ from rosidl_parser.definition import Message
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=message, include_directives=include_directives)
message=message, include_directives=include_directives, type_hash=type_hash)
}@

@[end for]@
Expand All @@ -55,25 +59,34 @@ TEMPLATE(
from rosidl_parser.definition import Service
}@
@[for service in content.get_elements_of_type(Service)]@

@{ hash_var = idl_structure_type_to_c_typename(service.namespaced_type) + '__TYPE_VERSION_HASH' }@
// Note: this define is for MSVC, where the static const var can't be used in downstream aggregate initializers
#define @(hash_var)__INIT @(type_hash_to_c_definition(type_hash['service'], line_final_backslash=True))
static const rosidl_type_hash_t @(hash_var) = @(hash_var)__INIT;
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=service.request_message, include_directives=include_directives)
message=service.request_message, include_directives=include_directives,
type_hash=type_hash['request_message'])
}@
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=service.response_message, include_directives=include_directives)
message=service.response_message, include_directives=include_directives,
type_hash=type_hash['response_message'])
}@
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=service.event_message, include_directives=include_directives)
message=service.event_message, include_directives=include_directives,
type_hash=type_hash['event_message'])
}@
@[end for]@
Expand All @@ -85,74 +98,99 @@ TEMPLATE(
from rosidl_parser.definition import Action
}@
@[for action in content.get_elements_of_type(Action)]@
@{ hash_var = idl_structure_type_to_c_typename(action.namespaced_type) + '__TYPE_VERSION_HASH' }@
#define @(hash_var)__INIT @(type_hash_to_c_definition(type_hash['action'], line_final_backslash=True))
static const rosidl_type_hash_t @(hash_var) = @(hash_var)__INIT;
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.goal, include_directives=include_directives)
message=action.goal, include_directives=include_directives,
type_hash=type_hash['goal'])
}@
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.result, include_directives=include_directives)
message=action.result, include_directives=include_directives,
type_hash=type_hash['result'])
}@
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.feedback, include_directives=include_directives)
message=action.feedback, include_directives=include_directives,
type_hash=type_hash['feedback'])
}@
@{ hash_var = idl_structure_type_to_c_typename(action.send_goal_service.namespaced_type) + '__TYPE_VERSION_HASH' }@
// Note: this define is for MSVC, where the static const var can't be used in downstream aggregate initializers
#define @(hash_var)__INIT @(type_hash_to_c_definition(type_hash['send_goal_service']['service'], line_final_backslash=True))
static const rosidl_type_hash_t @(hash_var) = @(hash_var)__INIT;

@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.send_goal_service.request_message, include_directives=include_directives)
message=action.send_goal_service.request_message, include_directives=include_directives,
type_hash=type_hash['send_goal_service']['request_message'])
}@

@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.send_goal_service.response_message, include_directives=include_directives)
message=action.send_goal_service.response_message, include_directives=include_directives,
type_hash=type_hash['send_goal_service']['response_message'])
}@

@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.send_goal_service.event_message, include_directives=include_directives)
message=action.send_goal_service.event_message, include_directives=include_directives,
type_hash=type_hash['send_goal_service']['event_message'])
}@

@{ hash_var = idl_structure_type_to_c_typename(action.get_result_service.namespaced_type) + '__TYPE_VERSION_HASH' }@
// Note: this define is for MSVC, where the static const var can't be used in downstream aggregate initializers
#define @(hash_var)__INIT @(type_hash_to_c_definition(type_hash['get_result_service']['service'], line_final_backslash=True))
static const rosidl_type_hash_t @(hash_var) = @(hash_var)__INIT;
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.get_result_service.request_message, include_directives=include_directives)
message=action.get_result_service.request_message, include_directives=include_directives,
type_hash=type_hash['get_result_service']['request_message'])
}@
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.get_result_service.response_message, include_directives=include_directives)
message=action.get_result_service.response_message, include_directives=include_directives,
type_hash=type_hash['get_result_service']['response_message'])
}@
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.get_result_service.event_message, include_directives=include_directives)
message=action.get_result_service.event_message, include_directives=include_directives,
type_hash=type_hash['get_result_service']['event_message'])
}@
@{
TEMPLATE(
'msg__struct.h.em',
package_name=package_name, interface_path=interface_path,
message=action.feedback_message, include_directives=include_directives)
message=action.feedback_message, include_directives=include_directives,
type_hash=type_hash['feedback_message'])
}@
@[end for]@
Expand Down
7 changes: 7 additions & 0 deletions rosidl_generator_c/resource/msg__struct.h.em
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ from rosidl_generator_c import idl_structure_type_sequence_to_c_typename
from rosidl_generator_c import idl_structure_type_to_c_include_prefix
from rosidl_generator_c import idl_structure_type_to_c_typename
from rosidl_generator_c import interface_path_to_string
from rosidl_generator_c import type_hash_to_c_definition
from rosidl_generator_c import value_to_c
}@
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Expand Down Expand Up @@ -62,6 +63,12 @@ for member in message.structure.members:
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Type Version Hash for interface
@{ hash_var = idl_structure_type_to_c_typename(message.structure.namespaced_type) + '__TYPE_VERSION_HASH' }@
// Note: this define is for MSVC, where the static const var can't be used in downstream aggregate initializers
#define @(hash_var)__INIT @(type_hash_to_c_definition(type_hash['message'], line_final_backslash=True))
static const rosidl_type_hash_t @(hash_var) = @(hash_var)__INIT;
// Constants defined in the message
@[for constant in message.constants]@
Expand Down
27 changes: 27 additions & 0 deletions rosidl_generator_c/rosidl_generator_c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from rosidl_generator_type_description import parse_rihs_string
from rosidl_generator_type_description import RIHS01_HASH_VALUE_SIZE
from rosidl_parser.definition import AbstractGenericString
from rosidl_parser.definition import AbstractSequence
from rosidl_parser.definition import AbstractString
Expand Down Expand Up @@ -219,3 +221,28 @@ def escape_string(s):

def escape_wstring(s):
return escape_string(s)


def type_hash_to_c_definition(hash_string, *, line_final_backslash=False):
"""Generate empy for rosidl_type_hash_t instance with 8 bytes per line for readability."""
bytes_per_row = 8
rows = 4
assert bytes_per_row * rows == RIHS01_HASH_VALUE_SIZE, 'This function is outdated.'
indent = 4 # Uncrustify prefers this indentation
version, value = parse_rihs_string(hash_string)
assert version == 1, 'This function only knows how to generate RIHS01 definitions.'

result = f'{{{version}, {{'
if line_final_backslash:
result += ' \\'
result += '\n'
for row in range(rows):
result += ' ' * (indent + 1)
for i in range(row * bytes_per_row, (row + 1) * bytes_per_row):
result += f' 0x{value[i * 2]}{value[i * 2 + 1]},'
if line_final_backslash:
result += ' \\'
result += '\n'
result += ' ' * indent
result += '}}'
return result
1 change: 1 addition & 0 deletions rosidl_generator_cpp/cmake/register_cpp.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

macro(rosidl_generator_cpp_extras BIN GENERATOR_FILES TEMPLATE_DIR)
find_package(ament_cmake_core QUIET REQUIRED)
find_package(rosidl_generator_type_description QUIET REQUIRED)
ament_register_extension(
"rosidl_generate_idl_interfaces"
"rosidl_generator_cpp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ rosidl_write_generator_arguments(
OUTPUT_DIR "${_output_path}"
TEMPLATE_DIR "${rosidl_generator_cpp_TEMPLATE_DIR}"
TARGET_DEPENDENCIES ${target_dependencies}
TYPE_HASH_TUPLES "${${rosidl_generate_interfaces_TARGET}__HASH_TUPLES}"
)

find_package(Python3 REQUIRED COMPONENTS Interpreter)
Expand All @@ -99,6 +100,9 @@ add_custom_target(
DEPENDS
${_generated_headers}
)
add_dependencies(
${rosidl_generate_interfaces_TARGET}__cpp
${rosidl_generate_interfaces_TARGET}__rosidl_generator_type_description)

set(_target_suffix "__rosidl_generator_cpp")
add_library(${rosidl_generate_interfaces_TARGET}${_target_suffix} INTERFACE)
Expand Down
1 change: 1 addition & 0 deletions rosidl_generator_cpp/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

<exec_depend>ament_index_python</exec_depend>
<exec_depend>rosidl_cli</exec_depend>
<exec_depend>rosidl_generator_type_description</exec_depend>
<exec_depend>rosidl_parser</exec_depend>

<test_depend>ament_lint_auto</test_depend>
Expand Down
21 changes: 15 additions & 6 deletions rosidl_generator_cpp/resource/action__struct.hpp.em
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@# Included from rosidl_generator_cpp/resource/idl__struct.hpp.em
@{
from rosidl_generator_c import type_hash_to_c_definition
from rosidl_parser.definition import ACTION_FEEDBACK_MESSAGE_SUFFIX
from rosidl_parser.definition import ACTION_FEEDBACK_SUFFIX
from rosidl_parser.definition import ACTION_GOAL_SERVICE_SUFFIX
Expand All @@ -17,42 +18,48 @@ action_name = '::'.join(action.namespaced_type.namespaced_name())
TEMPLATE(
'msg__struct.hpp.em',
package_name=package_name, interface_path=interface_path,
message=action.goal, include_directives=include_directives)
message=action.goal, include_directives=include_directives,
type_hash=type_hash['goal'])
}@

@{
TEMPLATE(
'msg__struct.hpp.em',
package_name=package_name, interface_path=interface_path,
message=action.result, include_directives=include_directives)
message=action.result, include_directives=include_directives,
type_hash=type_hash['result'])
}@

@{
TEMPLATE(
'msg__struct.hpp.em',
package_name=package_name, interface_path=interface_path,
message=action.feedback, include_directives=include_directives)
message=action.feedback, include_directives=include_directives,
type_hash=type_hash['feedback'])
}@

@{
TEMPLATE(
'srv__struct.hpp.em',
package_name=package_name, interface_path=interface_path,
service=action.send_goal_service, include_directives=include_directives)
service=action.send_goal_service, include_directives=include_directives,
type_hash=type_hash['send_goal_service'])
}@

@{
TEMPLATE(
'srv__struct.hpp.em',
package_name=package_name, interface_path=interface_path,
service=action.get_result_service, include_directives=include_directives)
service=action.get_result_service, include_directives=include_directives,
type_hash=type_hash['get_result_service'])
}@

@{
TEMPLATE(
'msg__struct.hpp.em',
package_name=package_name, interface_path=interface_path,
message=action.feedback_message, include_directives=include_directives)
message=action.feedback_message, include_directives=include_directives,
type_hash=type_hash['feedback_message'])
}@

@[for header_file in action_includes]@
Expand All @@ -72,6 +79,8 @@ namespace @(ns)
@[end for]@
struct @(action.namespaced_type.name)
{
static constexpr const rosidl_type_hash_t TYPE_VERSION_HASH = @(type_hash_to_c_definition(type_hash['action']));

/// The goal message defined in the action definition.
using Goal = @(action_name)@(ACTION_GOAL_SUFFIX);
/// The result message defined in the action definition.
Expand Down
Loading

0 comments on commit b93c518

Please sign in to comment.