diff --git a/rpcl2-derive/Cargo.toml b/rpcl2-derive/Cargo.toml index 867373d..6aa4b0d 100644 --- a/rpcl2-derive/Cargo.toml +++ b/rpcl2-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rpcl2-derive" description = "Derive macros for ros_pointcloud2 crate." -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Christopher Sieh "] homepage = "https://github.com/stelzo/ros_pointcloud2" diff --git a/rpcl2-derive/README.md b/rpcl2-derive/README.md index 5f9b5c2..adf3c26 100644 --- a/rpcl2-derive/README.md +++ b/rpcl2-derive/README.md @@ -5,4 +5,4 @@

-This crate should be used in combination with the `ros_pointcloud2` crate. +This crate is within `ros_pointcloud2` to implement the `PointConvertible` trait for custom points. The layouting part of it is heavily inspired by the [type-layout](https://crates.io/crates/type-layout) crate. \ No newline at end of file diff --git a/rpcl2-derive/src/lib.rs b/rpcl2-derive/src/lib.rs index 20a123b..dfc2dc5 100644 --- a/rpcl2-derive/src/lib.rs +++ b/rpcl2-derive/src/lib.rs @@ -1,12 +1,12 @@ -//! This crate provides macros for `ros_pointcloud2` and should be used in combination with said crate. +//! This crate provides macros for `ros_pointcloud2`. extern crate proc_macro; use std::collections::HashMap; use proc_macro::TokenStream; use proc_macro2::{Ident, Literal}; -use quote::{quote, quote_spanned, ToTokens}; -use syn::{parenthesized, parse_macro_input, spanned::Spanned, Data, DeriveInput, Fields, LitStr}; +use quote::{quote, ToTokens}; +use syn::{parenthesized, parse_macro_input, Data, DeriveInput, Fields, LitStr}; fn get_allowed_types() -> HashMap<&'static str, usize> { let mut allowed_datatypes = HashMap::<&'static str, usize>::new(); @@ -59,39 +59,6 @@ fn struct_field_rename_array(input: &DeriveInput) -> Vec { field_names } -/// This macro implements the `Fields` trait which is a subset of the `PointConvertible` trait. -/// It is useful for points that convert the `From` trait themselves but want to use this macro for not repeating the field names. -/// -/// You can rename the fields with the `rename` attribute. -/// -/// Use the rename attribute if your struct field name should be different to the ROS field name. -#[proc_macro_derive(Fields, attributes(rpcl2))] -pub fn ros_point_fields_derive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let struct_name = &input.ident; - - let field_names = struct_field_rename_array(&input) - .into_iter() - .map(|field_name| { - quote! { #field_name } - }); - - let field_names_len = field_names.len(); - - let expanded = quote! { - impl Fields<#field_names_len> for #struct_name { - fn field_names_ordered() -> [&'static str; #field_names_len] { - [ - #(#field_names,)* - ] - } - } - }; - - // Return the generated implementation - expanded.into() -} - /// This macro implements the `PointConvertible` trait for your struct so you can use your point for the PointCloud2 conversion. /// /// The struct field names are used in the message if you do not use the `rename` attribute for a custom name. @@ -124,6 +91,11 @@ pub fn ros_point_derive(input: TokenStream) -> TokenStream { for field in fields.iter() { let ty = field.ty.to_token_stream().to_string(); + if ty.contains("RGB") || ty.contains("rgb") { + return syn::Error::new_spanned(field, "RGB can not be guaranteed to have the correct type or layout. Implement PointConvertible manual or use predefined points instead.") + .to_compile_error() + .into(); + } if !allowed_datatypes.contains_key(&ty.as_str()) { return syn::Error::new_spanned(field, "Field type not allowed") .to_compile_error() @@ -132,19 +104,32 @@ pub fn ros_point_derive(input: TokenStream) -> TokenStream { } let field_len_token: usize = fields.len(); + let rename_arr = struct_field_rename_array(&input); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let layout = layout_of_type(&name, &input.data); - let field_names = struct_field_rename_array(&input) - .into_iter() - .map(|field_name| { - quote! { #field_name } - }); + let expanded = quote! { + impl #impl_generics ::ros_pointcloud2::PointConvertible<#field_len_token> for #name #ty_generics #where_clause { + fn layout() -> ::ros_pointcloud2::LayoutDescription { + let mut last_field_end = 0; + let mut fields = Vec::new(); - let field_impl = quote! { - impl ros_pointcloud2::Fields<#field_len_token> for #name { - fn field_names_ordered() -> [&'static str; #field_len_token] { - [ - #(#field_names,)* - ] + #layout + + let mut rename_idx = 0; + let rename_arr = vec![#(#rename_arr),*]; + let field_info: Vec<::ros_pointcloud2::LayoutField> = fields.into_iter().map(|field| { + match field { + (0, ty, size) => { + rename_idx += 1; + ::ros_pointcloud2::LayoutField::new(rename_arr[rename_idx - 1], ty, size) + }, + (1, _, size) => ::ros_pointcloud2::LayoutField::padding(size), + _ => unreachable!(), + } + }).collect(); + + ::ros_pointcloud2::LayoutDescription::new(field_info.as_slice()) } } }; @@ -184,72 +169,31 @@ pub fn ros_point_derive(input: TokenStream) -> TokenStream { } }; - let convertible = quote! { - impl ros_pointcloud2::PointConvertible<#field_len_token> for #name {} - }; - TokenStream::from(quote! { - #field_impl + #expanded #from_my_point #from_custom_point - #convertible }) } -#[proc_macro_derive(TypeLayout)] -pub fn derive_type_layout(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let name = input.ident; - - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let layout = layout_of_type(&name, &input.data); - - let expanded = quote! { - impl #impl_generics ::ros_pointcloud2::TypeLayout for #name #ty_generics #where_clause { - fn layout() -> ::ros_pointcloud2::LayoutDescription { - let mut last_field_end = 0; - let mut fields = Vec::new(); - - #layout - - ::ros_pointcloud2::LayoutDescription::new(fields.as_slice()) - } - } - }; - - TokenStream::from(expanded) -} - fn layout_of_type(struct_name: &Ident, data: &Data) -> proc_macro2::TokenStream { match data { Data::Struct(data) => match &data.fields { Fields::Named(fields) => { let values = fields.named.iter().map(|field| { let field_name = field.ident.as_ref().unwrap(); - let field_name_str = Literal::string(&field_name.to_string()); let field_ty = &field.ty; let field_ty_str = Literal::string(&field_ty.to_token_stream().to_string()); + let field_ty = &field.ty; - quote_spanned! { field.span() => - #[allow(unused_assignments)] - { - let size = ::std::mem::size_of::<#field_ty>(); - let offset = ::ros_pointcloud2::memoffset::offset_of!(#struct_name, #field_name); - - if offset > last_field_end { - fields.push(::ros_pointcloud2::LayoutField::Padding { - size: offset - last_field_end - }); - } - - fields.push(::ros_pointcloud2::LayoutField::Field { - name: ::std::borrow::Cow::Borrowed(#field_name_str), - ty: ::std::borrow::Cow::Borrowed(#field_ty_str), - size, - }); - - last_field_end = offset + size; + quote! { + let size = ::core::mem::size_of::<#field_ty>(); + let offset = ::ros_pointcloud2::memoffset::offset_of!(#struct_name, #field_name); + if offset > last_field_end { + fields.push((1, "", offset - last_field_end)); } + fields.push((0, #field_ty_str, size)); + last_field_end = offset + size; } }); @@ -258,9 +202,7 @@ fn layout_of_type(struct_name: &Ident, data: &Data) -> proc_macro2::TokenStream let struct_size = ::std::mem::size_of::<#struct_name>(); if struct_size > last_field_end { - fields.push(::ros_pointcloud2::LayoutField::Padding { - size: struct_size - last_field_end, - }); + fields.push((1, "", struct_size - last_field_end)); } } }