В настоящее время я вызываю нативную функцию из моего приложения Rust. Функция принимает обратный вызов, который принимает строку const char*
.
Я использую функцию трамплина для передачи в качестве собственного обратного вызова. Затем я передаю закрытие error_handler
как непрозрачный указатель (user_data
). Функция трамплина, когда вызывается собственной библиотекой, приводит непрозрачный указатель к типу замыкания и вызывает его со строкой C
.
В приведенном ниже коде, если я просто напечатаю значение C
строка, переданная в закрытие (error_handler
), все работает нормально.
Однако, если я фиксирую переменную error
в закрытии, при завершении области закрытия возникает исключение EXC_BAD ACCESS
. Выполнение приложения останавливается на библиотечной функции pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T)
Мне интересно, пытается ли закрытие error_handler
отбросить ссылку error
, которая у него есть, которая вызывает его взрыв, но я действительно не уверен.
Итак, где я сейчас нахожусь, я могу успешно распечатать значение строки C
, но я не могу захватить это значение во внешней области (что является всей причиной почему я использую функцию батута.
Кроме того, хотя этот вопрос не задается, если у кого-то есть лучшее решение, я бы очень хотел его услышать.
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 );
}
}
}