ri/c/macros.rs
1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri 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 Ri 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 with pointer registry support
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 let ptr = Box::into_raw(Box::new(<$c_type>::new(obj)));
209 $crate::c::register_ptr(ptr as usize);
210 ptr
211 }
212 };
213}
214
215/// Macro to generate C destructor function with pointer registry support
216///
217/// # Security
218///
219/// This macro generates a destructor that:
220/// 1. Checks if the pointer is null
221/// 2. Validates the pointer is registered (prevents double-free)
222/// 3. Unregisters the pointer before freeing (prevents use-after-free)
223#[macro_export]
224macro_rules! c_destructor {
225 ($fn_name:ident, $c_type:ty) => {
226 #[no_mangle]
227 pub extern "C" fn $fn_name(obj: *mut $c_type) {
228 if obj.is_null() {
229 return;
230 }
231
232 if !$crate::c::unregister_ptr(obj as usize) {
233 log::warn!(
234 "[Ri.C] Attempted to free unregistered or already freed pointer: {:?}",
235 obj
236 );
237 return;
238 }
239
240 unsafe {
241 let _ = Box::from_raw(obj);
242 }
243 }
244 };
245}
246
247/// Macro to generate C getter for string with pointer validation
248#[macro_export]
249macro_rules! c_string_getter {
250 ($fn_name:ident, $c_type:ty, $getter:expr) => {
251 #[no_mangle]
252 pub extern "C" fn $fn_name(obj: *mut $c_type) -> *mut std::ffi::c_char {
253 if obj.is_null() || !$crate::c::is_valid_ptr(obj as usize) {
254 return std::ptr::null_mut();
255 }
256 unsafe {
257 let val = $getter(&(*obj).inner);
258 match std::ffi::CString::new(val) {
259 Ok(c_str) => c_str.into_raw(),
260 Err(_) => std::ptr::null_mut(),
261 }
262 }
263 }
264 };
265}
266
267/// Macro to generate C setter for string with pointer validation
268#[macro_export]
269macro_rules! c_string_setter {
270 ($fn_name:ident, $c_type:ty, $setter:expr) => {
271 #[no_mangle]
272 pub extern "C" fn $fn_name(
273 obj: *mut $c_type,
274 value: *const std::ffi::c_char,
275 ) -> std::ffi::c_int {
276 if obj.is_null() || !$crate::c::is_valid_ptr(obj as usize) || value.is_null() {
277 return -1;
278 }
279 unsafe {
280 let val_str = match std::ffi::CStr::from_ptr(value).to_str() {
281 Ok(s) => s,
282 Err(_) => return -1,
283 };
284 $setter(&mut (*obj).inner, val_str);
285 0
286 }
287 }
288 };
289}