diff --git a/Cargo.toml b/Cargo.toml
index 8295cab3..53bf4f1b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@ members = [
"iceoryx2-bb/trait-tests",
"iceoryx2-ffi/ffi",
+ "iceoryx2-ffi/ffi-macros",
"iceoryx2-cal",
"iceoryx2",
@@ -66,7 +67,8 @@ iceoryx2-pal-configuration = { version = "0.3.0", path = "iceoryx2-pal/configura
iceoryx2-cal = { version = "0.3.0", path = "iceoryx2-cal" }
-iceoryx2-ffi = { version = "0.3.0", path = "iceoryx2-ffi" }
+iceoryx2-ffi = { version = "0.3.0", path = "iceoryx2-ffi/ffi" }
+iceoryx2-ffi-macros = { version = "0.3.0", path = "iceoryx2-ffi/ffi-macros" }
iceoryx2 = { version = "0.3.0", path = "iceoryx2/" }
diff --git a/iceoryx2-ffi/ffi-macros/Cargo.toml b/iceoryx2-ffi/ffi-macros/Cargo.toml
new file mode 100644
index 00000000..a8fc1882
--- /dev/null
+++ b/iceoryx2-ffi/ffi-macros/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "iceoryx2-ffi-macros"
+description = "iceoryx2: [internal] helper proc-macros for ffi"
+categories = { workspace = true }
+edition = { workspace = true }
+homepage = { workspace = true }
+keywords = { workspace = true }
+license = { workspace = true }
+readme = { workspace = true }
+repository = { workspace = true }
+rust-version = { workspace = true }
+version = { workspace = true }
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = { workspace = true }
+quote = { workspace = true }
+syn = { workspace = true }
diff --git a/iceoryx2-ffi/ffi-macros/src/lib.rs b/iceoryx2-ffi/ffi-macros/src/lib.rs
new file mode 100644
index 00000000..3e635168
--- /dev/null
+++ b/iceoryx2-ffi/ffi-macros/src/lib.rs
@@ -0,0 +1,216 @@
+// Copyright (c) 2024 Contributors to the Eclipse Foundation
+//
+// See the NOTICE file(s) distributed with this work for additional
+// information regarding copyright ownership.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Apache Software License 2.0 which is available at
+// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
+// which is available at https://opensource.org/licenses/MIT.
+//
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use proc_macro::TokenStream;
+use proc_macro2::TokenTree;
+use quote::{format_ident, quote};
+use syn::{parse_macro_input, punctuated::Punctuated, DeriveInput, LitStr, Meta, Token};
+
+#[proc_macro_attribute]
+pub fn iceoryx2_ffi(args: TokenStream, input: TokenStream) -> TokenStream {
+ let Args { rust_type: my_type } = parse_attribute_args(args);
+
+ // Parse the input tokens into a syntax tree
+ let my_struct = parse_macro_input!(input as DeriveInput);
+
+ let mut has_repr_c = false;
+ for attr in &my_struct.attrs {
+ if attr.path().is_ident("repr") {
+ let nested = attr
+ .parse_args_with(Punctuated::::parse_terminated)
+ .expect("'repr' should have at least one argument");
+ for meta in nested {
+ match meta {
+ // #[repr(C)]
+ Meta::Path(path) if path.is_ident("C") => {
+ has_repr_c = true;
+ }
+ _ => (),
+ }
+ }
+ }
+ }
+
+ if !has_repr_c {
+ panic!(
+ "The 'repr(C)' attribute is missing from '{}'!",
+ &my_struct.ident.to_string()
+ );
+ }
+
+ // Get the name of the struct we are generating code
+ let struct_name = &my_struct.ident;
+ let stripped_struct_name = struct_name
+ .to_string()
+ .strip_prefix("iox2_")
+ .expect("The struct must have an 'iox2_' prefix")
+ .strip_suffix("_t")
+ .expect("The struct must have a '_t' suffix")
+ .to_string();
+
+ // Define all the names we need
+ let struct_storage_name = format_ident!("iox2_{}_storage_t", stripped_struct_name);
+ let _struct_h_t_name = format_ident!("iox2_{}_h_t", stripped_struct_name);
+ let _struct_h_name = format_ident!("iox2_{}_h", stripped_struct_name);
+ let _struct_ref_h_t_name = format_ident!("iox2_{}_ref_h_t", stripped_struct_name);
+ let _struct_ref_h_name = format_ident!("iox2_{}_ref_h", stripped_struct_name);
+
+ // NOTE: cbindgen does not play well with adding new structs or fields to existing structs;
+ // this code is kept for reference
+
+ // // Add the additional fields to the struct
+ // match &mut my_struct.data {
+ // syn::Data::Struct(ref mut struct_data) => match &mut struct_data.fields {
+ // syn::Fields::Named(fields) => {
+ // fields.named.push(
+ // syn::Field::parse_named
+ // .parse2(quote! { value: #struct_storage_name })
+ // .unwrap(),
+ // );
+ // fields.named.push(
+ // syn::Field::parse_named
+ // .parse2(quote! { deleter: fn(*mut #struct_name) })
+ // .unwrap(),
+ // );
+ // }
+ // _ => (),
+ // },
+ // _ => panic!("#### `iceoryx_ffi` has to be used with structs "),
+ // };
+
+ let expanded = quote! {
+ impl #struct_storage_name {
+ const fn assert_storage_layout() {
+ static_assert_ge::<
+ { ::std::mem::align_of::<#struct_storage_name>() },
+ { ::std::mem::align_of::