0

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,
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
drkstr101
  • 760
  • 1
  • 6
  • 23
  • It looks like your question might be answered by the answers of [How do I create a Rust callback function to pass to a FFI function?](https://stackoverflow.com/q/31463426/155423); [Rust FFI passing trait object as context to call callbacks on](https://stackoverflow.com/q/33929079/155423); [How do I convert a Rust closure to a C-style callback?](https://stackoverflow.com/q/32270030/155423); [How do I pass a closure through raw pointers as an argument to a C function?](https://stackoverflow.com/q/38995701/155423). – Shepmaster Feb 19 '20 at 20:10
  • If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Feb 19 '20 at 20:10
  • 1
    Yup, I had read through a bunch of those but missed this one: https://stackoverflow.com/questions/33929079/rust-ffi-passing-trait-object-as-context-to-call-callbacks-on. My apologies. – drkstr101 Feb 19 '20 at 20:14
  • @Shepmaster I'm not sure how to close the question, but feel free to do so. Cheers! – drkstr101 Feb 19 '20 at 20:18

0 Answers0