From e7f5d47cc908250cf4fc882a3d9a4d4d422754f9 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 28 Feb 2024 12:17:51 +0100 Subject: [PATCH 1/6] Expose ROS2 constants in generated bindings (Rust and C++) --- .../ros2-bridge/msg-gen/src/types/message.rs | 52 +++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs b/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs index b8d811731..8a40c8e41 100644 --- a/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs +++ b/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs @@ -107,6 +107,28 @@ impl Constant { let value = self.r#type.value_tokens(&self.value); quote! { pub const #name: #type_ = #value; } } + + fn cxx_method_def_token_stream( + &self, + struct_name: &Ident, + package_name: &str, + ) -> impl ToTokens { + let name = format_ident!("const_{struct_name}_{}", self.name); + let cxx_name = format_ident!("const_{}", self.name); + let type_ = self.r#type.type_tokens(); + quote! { + #[namespace = #package_name] + #[cxx_name = #cxx_name] + pub fn #name (self: &#struct_name) -> #type_; + } + } + + fn cxx_method_impl_token_stream(&self, struct_name: &Ident) -> impl ToTokens { + let const_name = format_ident!("{}", self.name); + let name = format_ident!("const_{struct_name}_{}", self.name); + let type_ = self.r#type.type_tokens(); + quote! { pub fn #name (self: &ffi::#struct_name) -> #type_ { Self::#const_name }} + } } /// A message definition @@ -133,15 +155,29 @@ impl Message { let rust_type_def_inner = self.members.iter().map(|m| m.rust_type_def(&self.package)); let constants_def_inner = self.constants.iter().map(|c| c.token_stream()); + let cxx_const_def_inner = self + .constants + .iter() + .map(|c| c.cxx_method_def_token_stream(&struct_raw_name, package_name)); + let cxx_const_impl_inner = self + .constants + .iter() + .map(|c| c.cxx_method_impl_token_stream(&struct_raw_name)); let rust_type_default_inner = self.members.iter().map(|m| m.default_value()); - let attributes = if gen_cxx_bridge { - quote! { + let (attributes, cxx_consts) = if gen_cxx_bridge { + let attributes = quote! { #[namespace = #package_name] #[cxx_name = #cxx_name] - } + }; + let consts = quote! { + extern "Rust" { + #(#cxx_const_def_inner)* + } + }; + (attributes, consts) } else { - quote! {} + (quote! {}, quote! {}) }; if self.members.is_empty() { @@ -154,8 +190,16 @@ impl Message { pub struct #struct_raw_name { #(#rust_type_def_inner)* } + + #cxx_consts }; let impls = quote! { + impl ffi::#struct_raw_name { + #(#constants_def_inner)* + + #(#cxx_const_impl_inner)* + } + impl crate::_core::InternalDefault for ffi::#struct_raw_name { fn _default() -> Self { Self { From 5032a2b7b5ccbe9ed8f7b151ed6c1fab80f6d365 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 29 Feb 2024 11:35:07 +0100 Subject: [PATCH 2/6] Update `cxx`/`cxx-build` dependencies to `v1.0.118` --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2f623d04..031ee07f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,9 +1235,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.107" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe98ba1789d56fb3db3bee5e032774d4f421b685de7ba703643584ba24effbe" +checksum = "2673ca5ae28334544ec2a6b18ebe666c42a2650abfb48abbd532ed409a44be2b" dependencies = [ "cc", "cxxbridge-flags", @@ -1247,9 +1247,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.111" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bc81d2664db24cf1d35405f66e18a85cffd4d49ab930c71a5c6342a410f38c" +checksum = "9df46fe0eb43066a332586114174c449a62c25689f85a08f28fdcc8e12c380b9" dependencies = [ "cc", "codespan-reporting", @@ -1262,15 +1262,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.107" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20888d9e1d2298e2ff473cee30efe7d5036e437857ab68bbfea84c74dba91da2" +checksum = "886acf875df67811c11cd015506b3392b9e1820b1627af1a6f4e93ccdfc74d11" [[package]] name = "cxxbridge-macro" -version = "1.0.107" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa16a70dd58129e4dfffdff535fb1bce66673f7bbeec4a5a1765a504e1ccd84" +checksum = "1d151cc139c3080e07f448f93a1284577ab2283d2a44acd902c6fba9ec20b6de" dependencies = [ "proc-macro2", "quote", From e81a5703a327698eb7e49dfbd54e0435b5f9a895 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 29 Feb 2024 12:38:44 +0100 Subject: [PATCH 3/6] Make `const_` helper functions private to reemove them from the Rust API --- libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs b/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs index 8a40c8e41..91d61ac2b 100644 --- a/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs +++ b/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs @@ -127,7 +127,7 @@ impl Constant { let const_name = format_ident!("{}", self.name); let name = format_ident!("const_{struct_name}_{}", self.name); let type_ = self.r#type.type_tokens(); - quote! { pub fn #name (self: &ffi::#struct_name) -> #type_ { Self::#const_name }} + quote! { fn #name (self: &ffi::#struct_name) -> #type_ { Self::#const_name }} } } From 5909c4f7e6a6730fdaac74536b974c9e022a355b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 29 Feb 2024 13:10:43 +0100 Subject: [PATCH 4/6] Make C++ `const_` methods independent functions for convenience It's no longer required to create a struct instance to call them. --- .../ros2-bridge/msg-gen/src/types/message.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs b/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs index 91d61ac2b..01bc83276 100644 --- a/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs +++ b/libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs @@ -108,26 +108,25 @@ impl Constant { quote! { pub const #name: #type_ = #value; } } - fn cxx_method_def_token_stream( - &self, - struct_name: &Ident, - package_name: &str, - ) -> impl ToTokens { - let name = format_ident!("const_{struct_name}_{}", self.name); - let cxx_name = format_ident!("const_{}", self.name); + fn cxx_method_def_token_stream(&self, struct_name: &str, package_name: &str) -> impl ToTokens { + let name = format_ident!("const_{package_name}__{struct_name}_{}", self.name); + let cxx_name = format_ident!("const_{struct_name}_{}", self.name); let type_ = self.r#type.type_tokens(); quote! { #[namespace = #package_name] #[cxx_name = #cxx_name] - pub fn #name (self: &#struct_name) -> #type_; + pub fn #name () -> #type_; } } - fn cxx_method_impl_token_stream(&self, struct_name: &Ident) -> impl ToTokens { + fn cxx_method_impl_token_stream(&self, struct_raw_name: &Ident) -> impl ToTokens { let const_name = format_ident!("{}", self.name); - let name = format_ident!("const_{struct_name}_{}", self.name); + let name = format_ident!("const_{struct_raw_name}_{}", self.name); let type_ = self.r#type.type_tokens(); - quote! { fn #name (self: &ffi::#struct_name) -> #type_ { Self::#const_name }} + quote! { + #[allow(non_snake_case, dead_code)] + fn #name () -> #type_ { ffi::#struct_raw_name::#const_name } + } } } @@ -158,7 +157,7 @@ impl Message { let cxx_const_def_inner = self .constants .iter() - .map(|c| c.cxx_method_def_token_stream(&struct_raw_name, package_name)); + .map(|c| c.cxx_method_def_token_stream(&self.name, package_name)); let cxx_const_impl_inner = self .constants .iter() @@ -197,7 +196,6 @@ impl Message { impl ffi::#struct_raw_name { #(#constants_def_inner)* - #(#cxx_const_impl_inner)* } impl crate::_core::InternalDefault for ffi::#struct_raw_name { @@ -214,6 +212,8 @@ impl Message { crate::_core::InternalDefault::_default() } } + + #(#cxx_const_impl_inner)* }; (def, impls) From 570badbc6607d029fbe9ff41f3de73e194178f3e Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 29 Feb 2024 13:11:09 +0100 Subject: [PATCH 5/6] Access a ROS2 message constant in example and assert its value --- examples/c++-ros2-dataflow/node-rust-api/main.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/c++-ros2-dataflow/node-rust-api/main.cc b/examples/c++-ros2-dataflow/node-rust-api/main.cc index 4d8d0cb77..6968c8771 100644 --- a/examples/c++-ros2-dataflow/node-rust-api/main.cc +++ b/examples/c++-ros2-dataflow/node-rust-api/main.cc @@ -78,6 +78,9 @@ int main() } } + // try to access a constant for testing + assert((sensor_msgs::const_NavSatStatus_STATUS_NO_FIX() == -1)); + std::cout << "GOODBYE FROM C++ node (using Rust API)" << std::endl; return 0; From 4175936854d28fcc22662fd75206050690c6b4ce Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 29 Feb 2024 13:17:17 +0100 Subject: [PATCH 6/6] Explain ROS2 constant access in C++ node API Readme --- apis/c++/node/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apis/c++/node/README.md b/apis/c++/node/README.md index c18fb2e0a..dbba11335 100644 --- a/apis/c++/node/README.md +++ b/apis/c++/node/README.md @@ -228,3 +228,15 @@ else } ``` +### Constants + +Some ROS2 message definitions define constants, e.g. to specify the values of an enum-like integer field. +The Dora ROS2 bridge exposes these constants in the generated bindings as functions. + +For example, the `STATUS_NO_FIX` constant of the [`NavSatStatus` message](https://docs.ros.org/en/jade/api/sensor_msgs/html/msg/NavSatStatus.html) can be accessed as follows: + +```c++ +assert((sensor_msgs::const_NavSatStatus_STATUS_NO_FIX() == -1)); +``` + +(Note: Exposing them as C++ constants is not possible because it's [not supported by `cxx` yet](https://github.com/dtolnay/cxx/issues/1051).)