Skip to content

Commit de367f3

Browse files
Runtime Interface Reflection: rosidl_dynamic_typesupport (#1)
* Initial Implementation Signed-off-by: methylDragon <[email protected]> * Implement ser and deser, and use type description message Signed-off-by: methylDragon <[email protected]> * Migrate to rosidl type description Signed-off-by: methylDragon <[email protected]> * Fix const and memory issues Signed-off-by: methylDragon <[email protected]> * Implement init type builder from description Signed-off-by: methylDragon <[email protected]> * Clean up package Signed-off-by: methylDragon <[email protected]> * Add name getters Signed-off-by: methylDragon <[email protected]> * Reorder ID arg Signed-off-by: methylDragon <[email protected]> * Add support for bounded strings Signed-off-by: methylDragon <[email protected]> * Update complex value insertion Signed-off-by: methylDragon <[email protected]> * Beautify Signed-off-by: methylDragon <[email protected]> * Handle wstring Signed-off-by: methylDragon <[email protected]> * Implement serialization support destroy Signed-off-by: methylDragon <[email protected]> * Implement methods to add complex member with builders Signed-off-by: methylDragon <[email protected]> * Add visibility macros Signed-off-by: methylDragon <[email protected]> * Only use general type construction and implement default values Signed-off-by: methylDragon <[email protected]> * Beautify Signed-off-by: methylDragon <[email protected]> * Use allocators for memory management Signed-off-by: methylDragon <[email protected]> * Add fixed string support Signed-off-by: methylDragon <[email protected]> * Fix C linkage Signed-off-by: methylDragon <[email protected]> * Migrate all void methods to use return types Signed-off-by: methylDragon <[email protected]> * Migrate remaining methods to use return types Signed-off-by: methylDragon <[email protected]> * Lint Signed-off-by: methylDragon <[email protected]> * Fix windows build Signed-off-by: methylDragon <[email protected]> * Move dynamic type support struct to rosidl and fix mem issues Signed-off-by: methylDragon <[email protected]> * Use "methods" instead of interface Signed-off-by: methylDragon <[email protected]> * fix comparison of wrong parameter to null Signed-off-by: William Woodall <[email protected]> * Add uchar header Signed-off-by: methylDragon <[email protected]> * Refactor to use ret types for description validity checks Signed-off-by: methylDragon <[email protected]> * Use create instead of init Signed-off-by: methylDragon <[email protected]> * Remove print functions Signed-off-by: methylDragon <[email protected]> * Add identifier function Signed-off-by: methylDragon <[email protected]> * Clean up style and README Signed-off-by: methylDragon <[email protected]> --------- Signed-off-by: methylDragon <[email protected]> Signed-off-by: William Woodall <[email protected]> Co-authored-by: William Woodall <[email protected]>
1 parent f0d8183 commit de367f3

19 files changed

+6804
-0
lines changed

CMakeLists.txt

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
project(rosidl_dynamic_typesupport)
3+
set(CMAKE_VERBOSE_MAKEFILE ON)
4+
5+
6+
# COMPILER SETTINGS ================================================================================
7+
# Default to C11
8+
if(NOT CMAKE_C_STANDARD)
9+
set(CMAKE_C_STANDARD 11)
10+
endif()
11+
12+
# Default to C++17
13+
if(NOT CMAKE_CXX_STANDARD)
14+
set(CMAKE_CXX_STANDARD 17)
15+
endif()
16+
17+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
18+
add_compile_options(-Wall -Wextra -Wpedantic)
19+
endif()
20+
21+
set(CMAKE_VERBOSE_MAKEFILE ON)
22+
23+
24+
# DEPS =============================================================================================
25+
find_package(ament_cmake REQUIRED)
26+
find_package(ament_cmake_ros REQUIRED)
27+
find_package(rcutils REQUIRED)
28+
find_package(rosidl_runtime_c REQUIRED)
29+
30+
31+
# TARGETS ==========================================================================================
32+
add_library(${PROJECT_NAME}
33+
"src/api/serialization_support.c"
34+
"src/api/dynamic_data.c"
35+
"src/api/dynamic_type.c"
36+
37+
"src/dynamic_message_type_support_struct.c"
38+
"src/identifier.c"
39+
"src/types.c"
40+
)
41+
if(WIN32)
42+
target_compile_definitions(${PROJECT_NAME}
43+
PRIVATE "ROSIDL_DYNAMIC_TYPESUPPORT_BUILDING_DLL")
44+
endif()
45+
target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
46+
target_include_directories(${PROJECT_NAME} PUBLIC
47+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
48+
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
49+
)
50+
target_link_libraries(${PROJECT_NAME} PUBLIC
51+
rcutils::rcutils
52+
rosidl_runtime_c::rosidl_runtime_c
53+
)
54+
55+
56+
# INSTALL AND EXPORT ===============================================================================
57+
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-export
58+
ARCHIVE DESTINATION lib
59+
LIBRARY DESTINATION lib
60+
RUNTIME DESTINATION bin
61+
)
62+
63+
install(
64+
DIRECTORY include/
65+
DESTINATION include/${PROJECT_NAME}
66+
)
67+
68+
ament_export_targets(${PROJECT_NAME}-export HAS_LIBRARY_TARGET)
69+
ament_export_dependencies(ament_cmake)
70+
ament_export_dependencies(ament_cmake_ros)
71+
ament_export_dependencies(rosidl_runtime_c)
72+
73+
ament_package()

README.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,118 @@
11
# rosidl_dynamic_typesupport
22
Unified Interface for Dynamic (Runtime) Typesupport and Serialization
3+
4+
This library provides a unified interface for creating and reflecting dynamic types and dynamic data based off those types **at runtime**, as well as the utilities that support those objects.
5+
6+
It is meant to be used with serialization support libraries that fulfill the interface.
7+
Properly implemented, a user should be able to, given a serialized buffer, the serialization library used to serialize it, and the buffer's message/type description, obtain a way to introspect/reflect its contents.
8+
9+
## Example Usage
10+
11+
```cpp
12+
// Init Serialization Support (in this case, using FastRTPS)
13+
rosidl_dynamic_typesupport_serialization_support_t * serialization_support =
14+
rosidl_dynamic_typesupport_serialization_support_create(
15+
rosidl_dynamic_typesupport_fastrtps_create_serialization_support_impl(),
16+
rosidl_dynamic_typesupport_fastrtps_create_serialization_support_interface());
17+
18+
// Configure Dynamic Type Builder
19+
rosidl_dynamic_typesupport_dynamic_type_builder_t * flat_builder = rosidl_dynamic_typesupport_dynamic_type_builder_create(serialization_support, "flat");
20+
rosidl_dynamic_typesupport_dynamic_type_builder_add_bool_member(flat_builder, 0, "bool_field");
21+
rosidl_dynamic_typesupport_dynamic_type_builder_add_int32_member(flat_builder, 1, "int32_field");
22+
rosidl_dynamic_typesupport_dynamic_type_builder_add_string_member(flat_builder, 2, "string_field");
23+
24+
// Create Dynamic Type
25+
rosidl_dynamic_typesupport_dynamic_type_t * flat_type =
26+
rosidl_dynamic_typesupport_dynamic_type_builder_build(flat_builder);
27+
rosidl_dynamic_typesupport_dynamic_type_builder_destroy(flat_builder);
28+
29+
// Create Dynamic Data
30+
rosidl_dynamic_typesupport_dynamic_data_t * flat_data =
31+
rosidl_dynamic_typesupport_dynamic_data_create_from_dynamic_type(flat_type);
32+
33+
// Dynamic Data Setters
34+
rosidl_dynamic_typesupport_dynamic_data_set_bool_value(flat_data, 0, true);
35+
rosidl_dynamic_typesupport_dynamic_data_set_int32_value(flat_data, 1, 42);
36+
rosidl_dynamic_typesupport_dynamic_data_set_string_value(flat_data, 2, "rar");
37+
38+
// Dynamic Data Getters
39+
bool a;
40+
int32_t b;
41+
char * c;
42+
43+
rosidl_dynamic_typesupport_dynamic_data_get_bool_value(flat_data, 0, &a); // true
44+
rosidl_dynamic_typesupport_dynamic_data_get_int32_value(flat_data, 1, &b); // 42
45+
rosidl_dynamic_typesupport_dynamic_data_get_string_value(flat_data, 2, &c); // "rar"
46+
47+
// Cleanup
48+
free(c);
49+
rosidl_dynamic_typesupport_dynamic_data_destroy(flat_data);
50+
51+
```
52+
53+
## Some Terminology
54+
55+
- Serialization support: What this library provides; A unified, serialization library agnostic interface
56+
- Serialization support library: The implementation of the serialization support interface, which this library calls into
57+
- Type description (aka message description): A description of the buffer that fulfills the [rosidl](https://design.ros2.org/articles/idl_interface_definition.html) spec (this typically takes the form of the `TypeDescription` struct generated from the [type_descripion_interfaces package](https://github.com/ros2/rcl_interfaces/tree/rolling/type_description_interfaces/msg))
58+
- Dynamic type: The serialization support library's best-effort internal representation of the type description
59+
- Dynamic type builder: The serialization support library's means of constructing the dynamic type (on a field-by-field basis)
60+
- Dynamic data: The serialization support library's internal representation of the serialized buffer, informed by the dynamic type
61+
62+
### Why **Dynamic** Typesupport?
63+
64+
- Dynamic: This library dynamically changes its internal behavior based off its runtime input (e.g. what serialization library is loaded to be used with it)
65+
- Typesupport: And it supports the creation of, and access of types
66+
67+
This package differs from the usual ROS 2 notion of "dynamic" typesupport in that it offers **runtime** functionalities that change based off its input (hence dynamic), rather than compile-time code generation like the "dynamic" `rosidl_typesupport_introspection` package does (which generates introspectable typesupports at compile time.)
68+
69+
Furthermore, since this package does not actually generate any code, it is stored separately from the [rosidl repository](https://github.com/ros2/rosidl).
70+
71+
## The Serialization Support Interface
72+
73+
The serialization support interface is supposed to be an interface that is generic enough to support run-time type reflection (or in other words, dynamic type support), and is written in `C` to allow for easy language binding with any downstream libraries that use it.
74+
75+
Its interface is inspired by [rosidl](https://design.ros2.org/articles/idl_interface_definition.html), and the [DDS XTypes 1.3 language bindings spec (pg. 175)](https://www.omg.org/spec/DDS-XTypes/1.3/PDF), but is still intended to be generic, and so should support non-DDS middlewares.
76+
77+
The Serialization Support includes, for each supported middleware:
78+
79+
- Setting type members (dynamic type)
80+
- Note the lack of ability to get type members
81+
- Getting and setting data members based off that type (dynamic data)
82+
- Support for sequences and nested members
83+
- Utilities
84+
85+
The interface makes heavy use of `void *` casting to abstract away any necessary objects used with each serialization library.
86+
87+
### A Note On Proper Usage
88+
89+
The serialization support capabilities of this library are meant to be used alongside a rosidl-compliant description of the message a buffer is meant to represent (the type description).
90+
91+
This is because there is a distinction between the rosidl description of a message, and what a serialization library can describe or express (in its dynamic type.)
92+
As such, the rosidl description should be the sole source of determine what specific dynamic data getter functions are called during the unpacking of the message.
93+
And the serialization support library is in charge of supporting mapping to and from what types it can support, and the rosidl types.
94+
95+
Consider the following, contrived example, where you have a message layout like so:
96+
97+
```
98+
int int_field
99+
```
100+
101+
Suppose you had a serialization library that only provisions support for raw bytes.
102+
In that case, the serialization support library written would probably end up treating the `int_field` field as raw bytes when constructing its dynamic type, which means that when the message gets deserialized, something must retain the knowledge that the `int_field` was supposed to be an `int` type.
103+
104+
This is precisely what the type description is for, which a user should programmatically traverse, and use to determine the appropriate `int` dynamic data getter to call.
105+
The serialization support library then should implement its `int` dynamic data getter to extract the field from the buffer (as raw bytes, since that is what the serialization library supports), and coerce the extracted type (raw bytes) to output an `int` value to return.
106+
107+
Additionally, this means that, in order to support such cases, the serialization library must either:
108+
109+
- Use the same member/field indexing as is used in the type description
110+
- Have an alternate means to find the field described in the type description (e.g. by matching field names)
111+
112+
Note: There was a consideration to populate the dynamic type/data objects with the type description instead.
113+
But as the type description and dynamic type encompasses two subtly different concerns (rosidl compliance, and serialization library specific type support, respectively), it makes sense to keep them separate.
114+
And this is especially true given the fact that users are supposed to use the type description to call the appropriate getters.
115+
116+
## Type IDs
117+
118+
The type IDs used by this library (in `types.h`) are pulling from the [type_descripion_interfaces](https://github.com/ros2/rcl_interfaces/tree/rolling/type_description_interfaces/msg) message definitions.

0 commit comments

Comments
 (0)