Browse Source

Redesign ROS2 message struct generator to support `cxx_bridge`

tags/v0.3.3-rc1
Philipp Oppermann 2 years ago
parent
commit
b9e5e533e0
Failed to extract signature
8 changed files with 202 additions and 25 deletions
  1. +1
    -0
      Cargo.lock
  2. +2
    -0
      libraries/extensions/ros2-bridge/Cargo.toml
  3. +44
    -1
      libraries/extensions/ros2-bridge/msg-gen-macro/src/lib.rs
  4. +83
    -6
      libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs
  5. +60
    -11
      libraries/extensions/ros2-bridge/msg-gen/src/types/package.rs
  6. +6
    -4
      libraries/extensions/ros2-bridge/msg-gen/src/types/primitives.rs
  7. +2
    -2
      libraries/extensions/ros2-bridge/msg-gen/src/types/sequences.rs
  8. +4
    -1
      libraries/extensions/ros2-bridge/src/lib.rs

+ 1
- 0
Cargo.lock View File

@@ -1696,6 +1696,7 @@ name = "dora-ros2-bridge"
version = "0.1.0"
dependencies = [
"array-init",
"cxx",
"dora-daemon",
"dora-ros2-bridge-msg-gen-macro",
"eyre",


+ 2
- 0
libraries/extensions/ros2-bridge/Cargo.toml View File

@@ -8,6 +8,7 @@ edition = "2021"
[features]
default = ["generate-messages"]
generate-messages = ["dep:dora-ros2-bridge-msg-gen-macro"]
cxx-bridge = ["dep:cxx"]
# enables examples that depend on a sourced ROS2 installation
ros2-examples = ["eyre", "tokio", "dora-daemon"]

@@ -24,6 +25,7 @@ tokio = { version = "1.29.1", features = ["full"], optional = true }
dora-daemon = { path = "../../../binaries/daemon", optional = true }
tracing = "0.1.37"
tracing-subscriber = "0.3.17"
cxx = { version = "1.0", optional = true }

[dev-dependencies]
rand = "0.8.5"


+ 44
- 1
libraries/extensions/ros2-bridge/msg-gen-macro/src/lib.rs View File

@@ -63,14 +63,57 @@ pub fn msg_include_all(input: TokenStream) -> TokenStream {
.map(Path::new)
.collect::<Vec<_>>();

let message_structs = get_packages(&paths)
.unwrap()
.iter()
.map(|v| v.struct_token_stream(config.create_cxx_bridge))
.collect::<Vec<_>>();
let aliases = get_packages(&paths)
.unwrap()
.iter()
.map(|v| v.aliases_token_stream())
.collect::<Vec<_>>();
let packages = get_packages(&paths)
.unwrap()
.iter()
.map(|v| v.token_stream(config.create_cxx_bridge))
.collect::<Vec<_>>();

let (attributes, imports) = if config.create_cxx_bridge {
(quote! { #[cxx::bridge] }, quote! {})
} else {
(
quote! {},
quote! {
use serde::{Serialize, Deserialize};
},
)
};

(quote! {
#(#packages)*
#attributes
pub mod ffi {
#imports

#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct U16String {
chars: Vec<u16>,
}

impl crate::_core::InternalDefault for U16String {
fn _default() -> Self {
Default::default()
}
}


#(#message_structs)*
}

#(#aliases)*


// #(#packages)*
})
.into()
}


+ 83
- 6
libraries/extensions/ros2-bridge/msg-gen/src/types/message.rs View File

@@ -1,5 +1,6 @@
use heck::SnakeCase;
use quote::{format_ident, quote, ToTokens};
use syn::Ident;

use super::{primitives::*, sequences::Array, ConstantType, MemberType};

@@ -122,9 +123,69 @@ pub struct Message {
}

impl Message {
pub fn token_stream_with_mod(&self) -> impl ToTokens {
pub fn struct_token_stream(&self, package_name: &Ident, gen_cxx_bridge: bool) -> impl ToTokens {
let cxx_name = format_ident!("{}", self.name);
let struct_raw_name = format_ident!("{package_name}__{}", self.name);

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 rust_type_default_inner = self.members.iter().map(|m| m.default_value());

let attributes = if gen_cxx_bridge {
quote! {
#[namespace = #package_name]
#[cxx_name = #cxx_name]
}
} else {
quote! {}
};

if self.members.is_empty() {
quote! {}
} else {
quote! {
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#attributes
pub struct #struct_raw_name {
#(#rust_type_def_inner)*
}

impl crate::_core::InternalDefault for #struct_raw_name {
fn _default() -> Self {
Self {
#(#rust_type_default_inner)*
}
}
}

impl std::default::Default for #struct_raw_name {
#[inline]
fn default() -> Self {
crate::_core::InternalDefault::_default()
}
}
}
}
}

pub fn alias_token_stream(&self, package_name: &Ident) -> impl ToTokens {
let cxx_name = format_ident!("{}", self.name);
let struct_raw_name = format_ident!("{package_name}__{}", self.name);

if self.members.is_empty() {
quote! {}
} else {
quote! {
pub use super::super::ffi::#struct_raw_name as #cxx_name;
}
}
}

pub fn token_stream_with_mod(&self, gen_cxx_bridge: bool) -> impl ToTokens {
let mod_name = format_ident!("_{}", self.name.to_snake_case());
let inner = self.token_stream();
let inner = self.token_stream_args(gen_cxx_bridge);

quote! {
pub use #mod_name::*;
mod #mod_name {
@@ -134,6 +195,10 @@ impl Message {
}

pub fn token_stream(&self) -> impl ToTokens {
self.token_stream_args(false)
}

pub fn token_stream_args(&self, gen_cxx_bridge: bool) -> impl ToTokens {
let rust_type = format_ident!("{}", self.name);
let raw_type = format_ident!("{}_Raw", self.name);
let raw_ref_type = format_ident!("{}_RawRef", self.name);
@@ -144,6 +209,13 @@ impl Message {
self.members.clone()
};

let attributes = if gen_cxx_bridge {
let namespace = &self.name;
quote! { #[cxx::bridge(namespace = #namespace)] }
} else {
quote! {}
};

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 rust_type_default_inner = self.members.iter().map(|m| m.default_value());
@@ -178,10 +250,15 @@ impl Message {
FFIToRust as _FFIToRust,
};

#[allow(non_camel_case_types)]
#[derive(std::fmt::Debug, std::clone::Clone, std::cmp::PartialEq, serde::Serialize, serde::Deserialize)]
pub struct #rust_type {
#(#rust_type_def_inner)*
pub use self::t::#rust_type;

#attributes
mod t {
#[allow(non_camel_case_types)]
#[derive(std::fmt::Debug, std::clone::Clone, std::cmp::PartialEq, serde::Serialize, serde::Deserialize)]
pub struct #rust_type {
#(#rust_type_def_inner)*
}
}

impl #rust_type {


+ 60
- 11
libraries/extensions/ros2-bridge/msg-gen/src/types/package.rs View File

@@ -26,13 +26,50 @@ impl Package {
self.messages.is_empty() && self.services.is_empty() && self.actions.is_empty()
}

fn messages_block(&self) -> impl ToTokens {
fn message_structs(&self, package_name: Ident, gen_cxx_bridge: bool) -> impl ToTokens {
if self.messages.is_empty() {
quote! {
// empty msg
}
} else {
let items = self.messages.iter().map(|v| v.token_stream_with_mod());
let items = self
.messages
.iter()
.map(|v| v.struct_token_stream(&package_name, gen_cxx_bridge));
quote! {
#(#items)*
}
}
}

fn message_aliases(&self, package_name: &Ident) -> impl ToTokens {
if self.messages.is_empty() {
quote! {
// empty msg
}
} else {
let items = self
.messages
.iter()
.map(|v| v.alias_token_stream(package_name));
quote! {
pub mod msg {
#(#items)*
}
}
}
}

fn messages_block(&self, gen_cxx_bridge: bool) -> impl ToTokens {
if self.messages.is_empty() {
quote! {
// empty msg
}
} else {
let items = self
.messages
.iter()
.map(|v| v.token_stream_with_mod(gen_cxx_bridge));
quote! {
pub mod msg {
#(#items)*
@@ -71,21 +108,33 @@ impl Package {
}
}

pub fn struct_token_stream(&self, gen_cxx_bridge: bool) -> impl ToTokens {
let package_name = Ident::new(&self.name, Span::call_site());
let message_structs = self.message_structs(package_name, gen_cxx_bridge);

quote! {
#message_structs
}
}

pub fn aliases_token_stream(&self) -> impl ToTokens {
let package_name = Ident::new(&self.name, Span::call_site());
let aliases = self.message_aliases(&package_name);

quote! {
pub mod #package_name {
#aliases
}
}
}

pub fn token_stream(&self, gen_cxx_bridge: bool) -> impl ToTokens {
let name = Ident::new(&self.name, Span::call_site());
let messages_block = self.messages_block();
let messages_block = self.messages_block(gen_cxx_bridge);
let services_block = self.services_block();
let actions_block = self.actions_block();

let attributes = if gen_cxx_bridge {
let namespace = &self.name;
quote! { #[cxx::bridge(namespace = #namespace)] }
} else {
quote! {}
};

quote! {
#attributes
pub mod #name {
#messages_block
#services_block


+ 6
- 4
libraries/extensions/ros2-bridge/msg-gen/src/types/primitives.rs View File

@@ -133,7 +133,8 @@ impl NamedType {
let package = Ident::new(package, Span::call_site());
let namespace = Ident::new("msg", Span::call_site());
let name = Ident::new(&self.0, Span::call_site());
quote! { crate::#package::#namespace::#name }
let ident = format_ident!("{package}__{name}");
quote! { #ident }
}

fn raw_type_tokens(&self, package: &str) -> impl ToTokens {
@@ -175,7 +176,8 @@ impl NamespacedType {
let package = Ident::new(&self.package, Span::call_site());
let namespace = Ident::new(&self.namespace, Span::call_site());
let name = Ident::new(&self.name, Span::call_site());
quote! { crate::#package::#namespace::#name }
let ident = format_ident!("{package}__{name}");
quote! { #ident }
}

fn raw_type_tokens(&self) -> impl ToTokens {
@@ -215,9 +217,9 @@ impl GenericString {

fn type_tokens(self) -> impl ToTokens {
if self.is_wide() {
quote! { crate::_core::string::U16String }
quote! { U16String }
} else {
quote! { ::std::string::String }
quote! { String }
}
}



+ 2
- 2
libraries/extensions/ros2-bridge/msg-gen/src/types/sequences.rs View File

@@ -41,7 +41,7 @@ pub struct Sequence {
impl Sequence {
pub fn type_tokens(&self, package: &str) -> impl ToTokens {
let inner_type = self.value_type.type_tokens(package);
quote! { std::vec::Vec<#inner_type> }
quote! { Vec<#inner_type> }
}

pub fn raw_type_tokens(&self, package: &str) -> impl ToTokens {
@@ -72,7 +72,7 @@ pub struct BoundedSequence {
impl BoundedSequence {
pub fn type_tokens(&self, package: &str) -> impl ToTokens {
let inner_type = self.value_type.type_tokens(package);
quote! { std::vec::Vec<#inner_type> }
quote! { Vec<#inner_type> }
}

pub fn raw_type_tokens(&self, package: &str) -> impl ToTokens {


+ 4
- 1
libraries/extensions/ros2-bridge/src/lib.rs View File

@@ -1,7 +1,10 @@
pub use ros2_client;
pub use rustdds;

#[cfg(feature = "generate-messages")]
#[cfg(all(feature = "generate-messages", feature = "cxx-bridge"))]
dora_ros2_bridge_msg_gen_macro::msg_include_all!(cxx_bridge = true);

#[cfg(all(feature = "generate-messages", not(feature = "cxx-bridge")))]
dora_ros2_bridge_msg_gen_macro::msg_include_all!();

pub mod _core;

Loading…
Cancel
Save