inline layouting for smaller PointConvertible impl

This commit is contained in:
stelzo 2024-05-20 00:35:54 +02:00
parent 9f6068125f
commit 080ced8816
No known key found for this signature in database
GPG Key ID: FC4EF89052319374
3 changed files with 44 additions and 102 deletions

View File

@ -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 <stelzo@steado.de>"]
homepage = "https://github.com/stelzo/ros_pointcloud2"

View File

@ -5,4 +5,4 @@
</p>
</p>
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.

View File

@ -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<String> {
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));
}
}
}