sparse_ir_capi/
macros.rs

1//! Common macros for opaque type implementations
2//!
3//! This module provides macros to generate common C API functions for opaque types,
4//! following libsparseir's DECLARE_OPAQUE_TYPE pattern.
5
6/// Debug print macro that only outputs if SPARSEIR_DEBUG is set
7///
8/// Usage: `debug_println!("format string", args...)`
9#[macro_export]
10macro_rules! debug_println {
11    ($($arg:tt)*) => {
12        if std::env::var("SPARSEIR_DEBUG").is_ok() {
13            eprintln!("[SPARSEIR DEBUG] {}", format!($($arg)*));
14        }
15    };
16}
17
18/// Debug print macro for errors that only outputs if SPARSEIR_DEBUG is set
19///
20/// Usage: `debug_eprintln!("format string", args...)`
21#[macro_export]
22macro_rules! debug_eprintln {
23    ($($arg:tt)*) => {
24        if std::env::var("SPARSEIR_DEBUG").is_ok() {
25            eprintln!("[SPARSEIR DEBUG ERROR] {}", format!($($arg)*));
26        }
27    };
28}
29
30/// Generate common opaque type functions: release, clone, is_assigned, get_raw_ptr
31///
32/// This macro implements the standard lifecycle functions for opaque C API types,
33/// matching libsparseir's DECLARE_OPAQUE_TYPE pattern.
34///
35/// # Requirements
36/// - The type must implement `Clone`
37/// - The type name should follow the pattern `spir_*`
38///
39/// # Generated functions
40/// - `spir_<TYPE>_release()` - Drops the object
41/// - `spir_<TYPE>_clone()` - Creates a shallow copy (Arc-based, cheap)
42/// - `spir_<TYPE>_is_assigned()` - Checks if pointer is valid
43/// - `_spir_<TYPE>_get_raw_ptr()` - Returns raw pointer for debugging
44///
45/// # Example
46/// ```ignore
47/// // In types.rs
48/// #[derive(Clone)]
49/// #[repr(C)]
50/// pub struct spir_kernel {
51///     inner: KernelType,
52/// }
53///
54/// // In kernel.rs
55/// impl_opaque_type_common!(kernel);
56/// ```
57#[macro_export]
58macro_rules! impl_opaque_type_common {
59    ($type_name:ident) => {
60        paste::paste! {
61            /// Release the object by dropping it
62            ///
63            /// # Safety
64            /// The caller must ensure that the pointer is valid and not used after this call.
65            #[unsafe(no_mangle)]
66            pub extern "C" fn [<spir_ $type_name _release>](obj: *mut [<spir_ $type_name>]) {
67                if obj.is_null() {
68                    return;
69                }
70                // Convert back to Box and drop - safe because we check for null
71                unsafe {
72                    let _ = Box::from_raw(obj);
73                }
74            }
75
76            /// Clone the object (shallow copy with Arc reference counting)
77            ///
78            /// # Safety
79            /// The caller must ensure that the source pointer is valid.
80            /// The returned pointer must be freed with `spir_<type>_release()`.
81            ///
82            /// # Returns
83            /// A new pointer to a cloned object, or null if input is null or panic occurs.
84            #[unsafe(no_mangle)]
85            pub extern "C" fn [<spir_ $type_name _clone>](
86                src: *const [<spir_ $type_name>]
87            ) -> *mut [<spir_ $type_name>] {
88                if src.is_null() {
89                    return std::ptr::null_mut();
90                }
91
92                let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
93                    let src_ref = &*src;
94                    let cloned = (*src_ref).clone();
95                    // Clone uses Arc reference counting (cheap operation)
96                    Box::into_raw(Box::new(cloned))
97                }));
98
99                result.unwrap_or(std::ptr::null_mut())
100            }
101
102            /// Check if the object pointer is valid (non-null and dereferenceable)
103            ///
104            /// # Returns
105            /// 1 if the object is valid, 0 otherwise
106            #[unsafe(no_mangle)]
107            pub extern "C" fn [<spir_ $type_name _is_assigned>](
108                obj: *const [<spir_ $type_name>]
109            ) -> i32 {
110                if obj.is_null() {
111                    return 0;
112                }
113
114                let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
115                    let _ = &*obj;
116                    1
117                }));
118
119                result.unwrap_or(0)
120            }
121
122            /// Get the raw pointer for debugging purposes (internal use only)
123            ///
124            /// # Returns
125            /// The raw pointer cast to `void*`, or null if input is null
126            #[unsafe(no_mangle)]
127            pub extern "C" fn [<_spir_ $type_name _get_raw_ptr>](
128                obj: *const [<spir_ $type_name>]
129            ) -> *const std::ffi::c_void {
130                if obj.is_null() {
131                    return std::ptr::null();
132                }
133
134                obj as *const std::ffi::c_void
135            }
136        }
137    };
138}