I find myself stuck trying to implement callbacks to self
that gets passed through FFI.
Please consider this example code:
extern crate ffi;
use std::ffi::CString;
use std::os::raw::c_void;
use ffi::{
ACameraDevice, ACameraDevice_ErrorStateCallback, ACameraDevice_StateCallback,
ACameraDevice_StateCallbacks, ACameraManager, ACameraManager_create, ACameraManager_openCamera,
};
struct NDKCamera {
_camera_mgr: *mut ACameraManager, // some irrelevant fields...
}
impl NDKCamera {
pub fn new() -> NDKCamera {
let _camera_mgr = unsafe { ACameraManager_create() };
// initialize other field...
let camera = NDKCamera { _camera_mgr };
// Create back facing camera device
let c_str = CString::new("").unwrap();
unsafe {
ACameraManager_openCamera(
_camera_mgr,
c_str.as_ptr(),
camera.get_device_listener(),
// TODO...
0 as *mut *mut ACameraDevice,
);
}
camera
}
pub fn on_device_state(&self, dev: *mut ACameraDevice) {
//TODO ...
}
pub fn on_device_error(&self, dev: *mut ACameraDevice, err: i32) {
// TODO ...
}
fn get_device_listener(&self) -> *mut ACameraDevice_StateCallbacks {
let on_disconnected: ACameraDevice_StateCallback = Some(on_device_state_changes);
let on_error: ACameraDevice_ErrorStateCallback = Some(on_device_error_changes);
let callbacks = ACameraDevice_StateCallbacks {
context: (0 as *mut c_void), // note this line!
onDisconnected: on_disconnected,
onError: on_error,
};
let static_ref: &'static mut ACameraDevice_StateCallbacks = Box::leak(Box::new(callbacks));
static_ref
}
}
#[no_mangle]
extern "C" fn on_device_state_changes(ctx: *mut c_void, dev: *mut ACameraDevice) {
// TODO...
// let camera = Box::from_raw(ctx);
// camera.on_device_state(dev);
}
#[no_mangle]
extern "C" fn on_device_error_changes(ctx: *mut c_void, dev: *mut ACameraDevice, err: i32) {
// TODO...
// let camera = Box::from_raw(ctx);
// camera.on_device_error(dev, err);
}
#[no_mangle]
pub extern "C" fn foo() {
let _ = NDKCamera::new();
}
This code compiles just fine, but if I change the noted line above to context: (self as *mut c_void)
I get:
error[E0606]: casting `&NDKCamera` as `*mut std::ffi::c_void` is invalid
--> native_app\src\lib.rs:61:22
|
| context: (self as *mut c_void), // note this line!
| ^^^^^^^^^^^^^^^^^^^^^
After some reading, I understand how leaking references to self
is dangerous, and why it's not allowed, but then how do I implement this pattern in Rust?
Specifically, how do I get a c_void
pointer from self, and how do I safely dereference that pointer in on_device_state_changes
and on_device_error_changes
so I can make the appropriate calls to self
?
Here are the relevant symbols defined in ffi
:
pub type ACameraDevice_StateCallback = ::std::option::Option<
unsafe extern "C" fn(context: *mut ::std::os::raw::c_void, device: *mut ACameraDevice),
>;
pub type ACameraDevice_ErrorStateCallback = ::std::option::Option<
unsafe extern "C" fn(
context: *mut ::std::os::raw::c_void,
device: *mut ACameraDevice,
error: ::std::os::raw::c_int,
),
>;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ACameraDevice_StateCallbacks {
pub context: *mut ::std::os::raw::c_void,
pub onDisconnected: ACameraDevice_StateCallback,
pub onError: ACameraDevice_ErrorStateCallback,
}