Плохой доступ после завершения закрытия - PullRequest
3 голосов
/ 06 августа 2020

В настоящее время я вызываю нативную функцию из моего приложения 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 ); 
        }
    }   
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...