dmsc/c/
macros.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # C API Macros
19//!
20//! This module provides procedural macros for generating C FFI (Foreign Function Interface)
21//! boilerplate code. These macros simplify the creation of C-compatible interfaces for Rust
22//! types, enabling seamless interoperability between Rust implementations and C/C++ consumers.
23//!
24//! The macro system addresses the fundamental impedance mismatch between Rust's ownership-based
25//! memory model and C's manual memory management paradigm. By generating appropriate wrapper
26//! structures, constructor functions, destructor functions, and property accessors, these
27//! macros reduce boilerplate while maintaining safety and correctness guarantees.
28//!
29//! ## Design Philosophy
30//!
31//! The macro system follows several key design principles:
32//!
33//! - **Opaque Pointers**: All wrapped types use opaque pointer patterns where C code cannot
34//!   access internal fields directly, preventing invalid memory access and ensuring encapsulation.
35//!
36//! - **Memory Safety**: Despite operating across language boundaries, the generated code
37//!   maintains Rust's memory safety guarantees through careful ownership management and
38//!   destruction semantics.
39//!
40//! - **Error Handling**: The generated functions return integer status codes (0 for success,
41//!   negative values for errors) following C conventions for error reporting.
42//!
43//! - **Null Safety**: All generated functions handle NULL pointers gracefully, returning
44//!   error codes or NULL outputs as appropriate rather than causing undefined behavior.
45//!
46//! - **Resource Cleanup**: Automatic resource cleanup through destructor functions prevents
47//!   memory leaks when C code properly releases allocated objects.
48//!
49//! ## Available Macros
50//!
51//! This module provides five primary macros:
52//!
53//! 1. **c_wrapper!**: Generates a C-compatible wrapper structure for Rust types
54//! 2. **c_constructor!**: Generates constructor functions for creating wrapped instances
55//! 3. **c_destructor!**: Generates destructor functions for cleaning up wrapped instances
56//! 4. **c_string_getter!**: Generates getter functions for string properties
57//! 5. **c_string_setter!**: Generates setter functions for string properties
58//!
59//! ## Memory Management Model
60//!
61//! The generated code follows a consistent memory management pattern:
62//!
63//! - **Allocation**: Constructor functions allocate objects on the heap using Box::into_raw
64//! - **Access**: C code receives raw pointers to the allocated objects
65//! - **Deallocation**: Destructor functions free heap allocations using Box::from_raw
66//!
67//! C code using these generated APIs must:
68//!
69//! 1. Call constructor functions to create instances
70//! 2. Pass returned pointers to all subsequent API calls
71//! 3. Call destructor functions before pointers go out of scope
72//!
73//! ## Thread Safety Considerations
74//!
75//! The generated wrapper structures themselves do not provide thread synchronization.
76//! Thread safety depends on the underlying Rust type implementations:
77//!
78//! - Types implementing Send + Sync can be safely shared across threads
79//! - Types without these traits may require additional synchronization in C code
80//! - Concurrent access to wrapped objects should be coordinated by the C caller
81//!
82//! ## Usage Pattern
83//!
84//! The typical usage pattern for wrapping a Rust type involves four steps:
85//!
86//! 1. Define the wrapper structure using c_wrapper!
87//!
88//! 2. Implement the Rust type with appropriate FFI-safe methods
89//!
90//! 3. Generate constructor using c_constructor!
91//!
92//! 4. Generate destructor using c_destructor!
93//!
94//! ## Example: Wrapping a Custom Type
95//!
96//! ```rust,ignore
97//! use crate::c::macros::{c_wrapper, c_constructor, c_destructor};
98//!
99//! // Step 1: Define the Rust type
100//! pub struct MyResource {
101//!     handle: i32,
102//!     name: String,
103//! }
104//!
105//! // Step 2: Generate C wrapper
106//! c_wrapper!(CMyResource, MyResource);
107//!
108//! // Step 3: Generate constructor (implemented separately)
109//! #[no_mangle]
110//! pub extern "C" fn my_resource_new() -> *mut CMyResource {
111//!     let resource = MyResource {
112//!         handle: 0,
113//!         name: String::new(),
114//!     };
115//!     Box::into_raw(Box::new(CMyResource::new(resource)))
116//! }
117//!
118//! // Step 4: Generate destructor
119//! c_destructor!(my_resource_free, CMyResource);
120//!
121//! // C code can now use:
122//! // - my_resource_create() to allocate
123//! // - my_resource_*() functions to operate
124//! // - my_resource_free() to deallocate
125//! ```
126//!
127//! ## Performance Characteristics
128//!
129//! The generated code has minimal performance overhead:
130//!
131//! - Wrapper structures are zero-cost abstractions (single pointer indirection)
132//! - Constructor/destructor calls are single heap allocations
133//! - Getter/setter operations are direct method calls on wrapped types
134//!
135//! The performance considerations primary are:
136//!
137//! - Heap allocation for object creation (amortized O(1))
138//! - Reference counting overhead for Rc/Arc types
139//! - Synchronization costs for thread-safe types
140//!
141//! ## Limitations and Constraints
142//!
143//! These macros have certain limitations:
144//!
145//! - Cannot generate wrappers for generic types directly
146//! - String getters assume UTF-8 encoding
147//! - Error handling is limited to integer return codes
148//! - No support for complex return types (use output parameters)
149//!
150//! For complex types requiring multiple return values, use output parameters:
151//!
152//! ```rust,ignore
153//! // Instead of returning complex types:
154//! // fn get_result() -> Result<ComplexType, Error>
155//!
156//! // Use output parameters:
157//! fn get_result(output: &mut ComplexType) -> c_int {
158//!     // fill in output parameter
159//!     0 // success
160//! }
161//! ```
162//!
163//! ## Dependencies
164//!
165//! This module is self-contained within the DMSC C API layer:
166//!
167//! - No external crate dependencies
168//! - Uses only standard library types (Box, std::ffi)
169//! - Compatible with no_std environments with allocator
170//!
171//! ## Feature Flags
172//!
173//! This module has no feature flags as it provides core FFI infrastructure
174//! that is always required for C API generation.
175
176/// Macro to generate C wrapper struct for a Rust type
177#[macro_export]
178macro_rules! c_wrapper {
179    ($c_name:ident, $rust_type:ty) => {
180        #[repr(C)]
181        pub struct $c_name {
182            inner: $rust_type,
183        }
184
185        impl $c_name {
186            pub fn new(inner: $rust_type) -> Self {
187                Self { inner }
188            }
189
190            pub fn inner(&self) -> &$rust_type {
191                &self.inner
192            }
193
194            pub fn inner_mut(&mut self) -> &mut $rust_type {
195                &mut self.inner
196            }
197        }
198    };
199}
200
201/// Macro to generate C constructor function
202#[macro_export]
203macro_rules! c_constructor {
204    ($fn_name:ident, $c_type:ty, $rust_type:ty, $new_expr:expr) => {
205        #[no_mangle]
206        pub extern "C" fn $fn_name() -> *mut $c_type {
207            let obj = $new_expr;
208            Box::into_raw(Box::new(<$c_type>::new(obj)))
209        }
210    };
211}
212
213/// Macro to generate C destructor function
214#[macro_export]
215macro_rules! c_destructor {
216    ($fn_name:ident, $c_type:ty) => {
217        #[no_mangle]
218        pub extern "C" fn $fn_name(obj: *mut $c_type) {
219            if !obj.is_null() {
220                unsafe {
221                    let _ = Box::from_raw(obj);
222                }
223            }
224        }
225    };
226}
227
228/// Macro to generate C getter for string
229#[macro_export]
230macro_rules! c_string_getter {
231    ($fn_name:ident, $c_type:ty, $getter:expr) => {
232        #[no_mangle]
233        pub extern "C" fn $fn_name(obj: *mut $c_type) -> *mut std::ffi::c_char {
234            if obj.is_null() {
235                return std::ptr::null_mut();
236            }
237            unsafe {
238                let val = $getter(&(*obj).inner);
239                match std::ffi::CString::new(val) {
240                    Ok(c_str) => c_str.into_raw(),
241                    Err(_) => std::ptr::null_mut(),
242                }
243            }
244        }
245    };
246}
247
248/// Macro to generate C setter for string
249#[macro_export]
250macro_rules! c_string_setter {
251    ($fn_name:ident, $c_type:ty, $setter:expr) => {
252        #[no_mangle]
253        pub extern "C" fn $fn_name(
254            obj: *mut $c_type,
255            value: *const std::ffi::c_char,
256        ) -> std::ffi::c_int {
257            if obj.is_null() || value.is_null() {
258                return -1;
259            }
260            unsafe {
261                let val_str = match std::ffi::CStr::from_ptr(value).to_str() {
262                    Ok(s) => s,
263                    Err(_) => return -1,
264                };
265                $setter(&mut (*obj).inner, val_str);
266                0
267            }
268        }
269    };
270}