How to pass a C function pointer for Rust to call it back later?

I'm learning about interfacing Rust and C++ (using the C interface). This is an example from Mozilla's guide for Rust on Android:

use std::os::raw::{c_char};
use std::ffi::{CString, CStr};

#[no_mangle]
pub extern fn rust_greeting(to: *const c_char) -> *mut c_char {
    let c_str = unsafe { CStr::from_ptr(to) };
    let recipient = match c_str.to_str() {
        Err(_) => "there",
        Ok(string) => string,
    };

    CString::new("Hello ".to_owned() + recipient).unwrap().into_raw()
}

as you can see, a C char pointer is expected, and it returns a C char pointer also.

I want to use a Rust code as a library on my project. This Rust code will deliver packets to me when they're ready. Therefore, I need a way to pass a callback to Rust so it can call it back when it has data. Kinda like this C++ snippet:

extern "C" onData(uint8_t* payload, size_t size) {
    //do something with payload
}

MyRustType myRustObject("initialization_string");
myRustObject.setOnDataCallback(&onData);
myRustObject.beginWork();

So, the rust 'class', MyRustType, will begin to work and deliver packets through onData. Is it possible to pass a C function as a pointer to Rust?

If not, I can pass the pointer as an uint64_t number, and make Rust pass this number + payload back to a C function, which in turn casts this uint64_t pointer into the function and calls it with the payload. But I think that since Rust is so C-friendly, there's a better way of doing this.

It's not clear to me whether you're asking what the Rust syntax is for function pointers, or something else. I think you want to do something like this:

impl MyRustType {
    fn set_on_data_callback(&mut self, callback: unsafe extern "C" fn(*mut u8, usize)) {
        // ...
    }
}
1 Like

looks like it's almost what I need. However, callback's type is function, not function pointer. Or does 'unsafe' make Rust interpret it as a C function pointer somehow?

If I do, in C:

void callMeBackAfter5Seconds(uint8_t* p, size_t size) {
    print("called\n");
}

set_on_data_callback(&callMeBackAfter5Seconds);
runSomeRustFunction();

then in Rust:

fn set_on_data_callback(&mut self, callback: unsafe extern "C" fn(*mut u8, usize)) {
        sleep(5);//5 seconds
        callback(buffer, buffer.size());
    }

then it'll call the callback after 5 seconds?

That should work! fn() is a function pointer, and unsafe extern "C" fn(...) is a function pointer with the C ABI which is unsafe to call. See the fn docs.

Thanks. I learned a lot. Do you perhaps know what's happening here? It's almost done

use std::os::raw::{c_int};
type OnDataCallback = unsafe extern "C" fn(data: *mut u8, len: usize) -> c_int;

static mut onDataCallback_: Option<OnDataCallback> = None;

#[no_mangle]
pub extern "C" fn registerOnDataCallback(cb: Option<OnDataCallback>) -> c_int
{
    unsafe{onDataCallback_ = cb;}
    return 0;
}

#[no_mangle]
pub extern "C" fn doSomething()
{
    let c_str_2 = std::ffi::CString::new(b"world" as &[u8]).unwrap();
    let c_ptr: *mut u8 = c_str_2.as_ptr() as *mut u8;
    
    unsafe{onDataCallback_.unwrap()(c_ptr , 100)};
}

and

#include <iostream>
typedef int (*onDataCallback_)(uint8_t *data, size_t len);

extern "C" int registerOnDataCallback(onDataCallback_ cb);

extern "C" int onDataCallback(uint8_t *data, size_t len) {
    std::cout << "called onData with size " + len << std::endl;
}

int main() {
    registerOnDataCallback(&onDataCallback);
    getchar();
    return 0;
}

but Im getting

root@e2c26cf9809d:/workspaces/smoltcp_with_c_interface/src# g++ -o rust_c -L. -linterface interface.cpp/tmp/ccdoryEA.o: In function `main':
interface.cpp:(.text+0x51): undefined reference to `registerOnDataCallback(int (*)(unsigned char*, unsigned long))'
collect2: error: ld returned 1 exit status

That's quite odd. It looks like it's trying to link to a C++-decorated version of the symbol. However, you did specify extern "C" in your declaration, so the C++ compiler should generate an unmangled symbol. Which C++ compiler and version are you using?

A couple questions

Did you compile the rust library? What folder is the compiled rust library in (the .so file) and what is the name of the file? It seems like you may not be linking in the rust library.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.