This example shows how to pass rust closure as C callback without need of Box type (nostd; no liballoc); Created for the the implementation of thread::spawn for FreeRTOS (rust-idf)
Only requirement - malloc and free functions
This example shows how to pass rust closure as C callback without need of Box type (nostd; no liballoc); Created for the the implementation of thread::spawn for FreeRTOS (rust-idf)
Only requirement - malloc and free functions
extern crate libc; // 0.2.65 | |
use core::ffi::c_void as CVoid; | |
use core::mem::MaybeUninit; | |
use core::ptr::drop_in_place; | |
struct CallbackHolder<F: FnOnce()> { | |
pub callback: Option<F>, | |
} | |
// wrapper funtion | |
fn test_callback_rust<F: FnOnce() + Send + 'static>(callback: F) { unsafe { | |
let holder = libc::malloc( | |
std::mem::size_of::<MaybeUninit<CallbackHolder<F>>>() | |
) as *mut MaybeUninit<CallbackHolder<F>>; | |
(*holder) = MaybeUninit::new(CallbackHolder{callback: Some(callback)}); | |
test_callback_c(c_fn::<F>, holder as *mut CVoid); | |
}} | |
// Proxy function for closure calls | |
extern "C" fn c_fn<F : FnOnce()>(data: *mut CVoid) { unsafe { | |
let holder = data as *mut MaybeUninit<CallbackHolder<F>>; | |
(*(&mut *holder).as_mut_ptr()).callback.take().unwrap()(); | |
drop_in_place((&mut *holder).as_mut_ptr()); | |
libc::free(holder as *mut CVoid); | |
}} | |
// Defined in C code | |
extern "C" fn test_callback_c(callback: extern "C" fn(*mut CVoid), data: *mut CVoid) { | |
callback(data); | |
} | |
struct DropStruct { | |
val: u32, | |
} | |
impl DropStruct { | |
pub fn new() -> DropStruct { | |
DropStruct { val: 42 } | |
} | |
pub fn print_self(&self) { | |
println!("I am alive {}!", self.val) | |
} | |
} | |
impl Drop for DropStruct { | |
fn drop(&mut self) { | |
println!("I have been dropped {}!", self.val) | |
} | |
} | |
fn main() { | |
let captured = DropStruct::new(); | |
test_callback_rust(move || { | |
captured.print_self(); | |
println!("Hello world!"); | |
}); | |
} |