From b5af5fbe70acaba9bf80f146dde8df61c2bde1f8 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Wed, 1 Feb 2023 13:10:52 +0100 Subject: [PATCH] Add custom macro for ReprC impl --- Cargo.toml | 1 + src/lib.rs | 3 ++ src/prelude.rs | 3 ++ src/repr_c.rs | 36 +++++++++++++++++++ src/reprc_proc_macro/Cargo.toml | 13 +++++++ src/reprc_proc_macro/src/lib.rs | 62 +++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+) create mode 100644 src/repr_c.rs create mode 100644 src/reprc_proc_macro/Cargo.toml create mode 100644 src/reprc_proc_macro/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index bca37ff..0f3db86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ rand = "0.8.5" backtrace = "0.3.67" assetpath = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } anyhow = { version = "1.0.68", features = ["backtrace"] } +reprc_proc_macro = { path = "src/reprc_proc_macro" } # networking serde = { version = "1.0.152", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs index 878d738..2e8dca8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,4 +14,7 @@ pub mod try_lock_guard; pub mod arc_unique_vec; pub mod rc_unique_vec; +#[macro_use] +mod repr_c; + pub mod unsafe_life_time; diff --git a/src/prelude.rs b/src/prelude.rs index b19f64d..4be0e39 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -27,3 +27,6 @@ pub use crate::try_lock_guard::TryLockGuard; // unsafe life time trickery pub use crate::unsafe_life_time::*; + +pub use crate::repr_c::ReprC; +pub use reprc_proc_macro::DeriveReprC; diff --git a/src/repr_c.rs b/src/repr_c.rs new file mode 100644 index 0000000..416166b --- /dev/null +++ b/src/repr_c.rs @@ -0,0 +1,36 @@ +use reprc_proc_macro::DeriveReprC; + +#[macro_export] +macro_rules! impl_reprc { + ( + $(#[$meta: meta])* + $v:vis struct $struct_name:ident { + $( $(#[$field_meta:meta])* $member:ident : $t:ty, )+ + } + ) => { + #[repr(C)] + #[derive(Debug, Clone, DeriveReprC)] + $(#[$meta])* + $v struct $struct_name { + $( + $( #[$field_meta] )* + pub $member: $t, + )+ + } + }; +} + +impl_reprc!( + struct Test { + #[assume_reprc] + i1: i32, + #[assume_reprc] + i2: i32, + #[assume_reprc] + v1: Vec, + } +); + +pub unsafe trait ReprC { + fn is_repr_c(&self) -> bool; +} diff --git a/src/reprc_proc_macro/Cargo.toml b/src/reprc_proc_macro/Cargo.toml new file mode 100644 index 0000000..3f44c9e --- /dev/null +++ b/src/reprc_proc_macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "reprc_proc_macro" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = { version = "1.0.107", features = ["full", "fold"] } +quote = "1.0.23" \ No newline at end of file diff --git a/src/reprc_proc_macro/src/lib.rs b/src/reprc_proc_macro/src/lib.rs new file mode 100644 index 0000000..6d5d9f9 --- /dev/null +++ b/src/reprc_proc_macro/src/lib.rs @@ -0,0 +1,62 @@ +use proc_macro::TokenStream; +use quote::{quote, ToTokens}; +use syn::{parse_macro_input, Data, DeriveInput, Ident}; + +const REPRC_ATTRIBUTE: &'static str = "assume_reprc"; + +#[proc_macro_derive(DeriveReprC, attributes(assume_reprc))] +pub fn reprc_builder(item: TokenStream) -> TokenStream { + let ast = parse_macro_input!(item as DeriveInput); + + let struct_name = ast.ident; + let generics = ast.generics.to_token_stream(); + + match ast.data { + Data::Struct(data_struct) => match data_struct.fields { + syn::Fields::Named(named_fields) => { + let reprc_fields: Vec = named_fields + .named + .iter() + .filter_map(|field| { + if field + .attrs + .iter() + .find(|attribute| { + let segments = &attribute.path.segments; + + if segments.len() < 1 { + return false; + } + + segments[0].ident == REPRC_ATTRIBUTE + }) + .is_some() + { + None + } else { + Some(field.ident.clone()) + } + }) + .map(|i| i.unwrap()) + .collect(); + + TokenStream::from(quote! { + unsafe impl #generics ReprC for #struct_name #generics { + fn is_repr_c(&self) -> bool { + true + + #( + && self. #reprc_fields.to_tokens() .is_repr_c() + )* + } + } + }) + } + syn::Fields::Unnamed(_) => todo!("Unnamed Field not supported"), + syn::Fields::Unit => todo!("Unit not supported"), + }, + + Data::Enum(_) => todo!("Enum not supported"), + Data::Union(_) => todo!("Union not supported"), + } +}