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}