I'm currently calling a native function from my Rust application. The function takes a callback which takes a const char* string.
I'm using a trampoline function to pass in as the native callback. I'm then passing in the closure error_handler as an opaque pointer (user_data). The trampoline function, when called by the native library, casts the opaque pointer to the closure type, and invokes it with the C string.
In the code below, if I just print the value of the C string passed to the closure (error_handler) everything works fine.
If, however, I capture the error variable in the closure, when the closure scope ends a EXC_BAD ACCESS exception is thrown. Execution of the application stops at the library function pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T)
I'm wondering if the error_handler closure is attempting to drop the error reference it has which is causing it to blow up but I'm really unsure.
So, where I'm at right now, I can successfully print the value of the C string but I'm unable to capture that value in the outer scope (which is the entire reason why I'm using the trampoline function.
Also, although this is not the question being asked, if someone has a better solution I'd really like to hear it.
Rust
impl MyStruct {
pub fn start( &self ) {
let mut error: Option<String> = None;
// Closure is invoked from trampoline function.
let mut error_handler = | errors: *const c_char | {
let c_str = unsafe { CStr::from_ptr( errors ) };
if let Ok( s ) = c_str.to_str( ).map( | s | s.to_owned( ) ) {
error = Some( s );
}
}; // <-- When the scope of the closure ends a BAD_ACCESS exception is thrown.
let trampoline = get_trampoline( &mut error_handler );
unsafe {
native_func( trampoline, &mut error_handler as *mut _ as *mut c_void );
}
if let Some( e ) = error { // Do something with error. }
}
}
type ErrorCallback = unsafe extern "C" fn( *const c_char, *mut c_void );
unsafe extern "C" fn trampoline<F>( error: *const c_char, user_data: *mut c_void)
where
F: FnMut( *const c_char ),
{
// Cast the user data to the closure passed in above.
let user_data = &mut *( user_data as *mut F );
user_data( error );
}
// Rust's version of decltype...
fn get_trampoline<F>( _ : F ) -> ErrorCallback
where
F: FnMut( *const c_char )
{
trampoline::<F>
}
#[link( name = "mylib" )]
extern {
fn native_func( error: ErrorCallback,
user_data: *mut c_void );
}
C++
extern "C"
void native_func( ErrorCallback error,
void* user_data )
{
try
{
// Call a function that intentionally throws to test the
// error callback.
}
catch( const std::exception& ex )
{
if ( error )
{
std::string e{ ex.what( ) };
error( e.data( ), user_data );
}
}
}