Passing a struct containing function pointers from Rust to C++

Hello good people,

I have a rather unusual use-case. I'm in middle of porting part of my application to Rust (from C++)
Let's say I have a struct defined in a header and present in a prebuilt .so file (C++).

struct A {
int (*cb1)(struct A *s),
void (*cb2)(struct A *s, int *data),
const char * (*cb3)(struct A *s, int *data),
const char * (*cb4)(struct A *s, int *data),
int (*cb5)(struct A *s, int *data),
}

This struct is to be initialized in Rust side and the pointer is to be sent to C++ side.
The library will then copy its own functions to the callbacks which are then be called from Rust side.

I'm not sure how to solve this problem. Any help would be highly appreciated.

Thank you

In Rust, Option<unsafe extern "C" fn(T) -> U> is equivalent to a nullable U (*)(T) function pointer in C/C++. So you'd express the struct like:

use std::os::raw::{c_char, c_int};

#[derive(Default)]
#[repr(C)]
struct A {
    cb1: Option<unsafe extern "C" fn(*mut A) -> c_int>,
    cb2: Option<unsafe extern "C" fn(*mut A, *mut c_int)>,
    cb3: Option<unsafe extern "C" fn(*mut A, *mut c_int) -> *const c_char>,
    cb4: Option<unsafe extern "C" fn(*mut A, *mut c_int) -> *const c_char>,
    cb5: Option<unsafe extern "C" fn(*mut A, *mut c_int) -> c_int>,
}

extern "C" {
    fn fill_struct(s: *mut A);
}

fn main() {
    let mut s = A::default();
    unsafe {
        fill_struct(&mut s);
    }
    let cb1 = s.cb1.unwrap();
    println!("{}", unsafe { cb1(&mut s) });
}
2 Likes

Hello LegionMammal978,

While I thank you for the reply but running that code, however, crashed!

By the way, I am not calling CPP functions as "extern C", rather I am using the following to load the library and call the C++ function

fn call_cpp_func(a: *mut A) -> Result<(), Box<dyn std::error::Error>> {
    let lib = libloading::Library::new("libname.so");
    let cpp_func: libloading::Symbol<unsafe extern fn(*mut A)> = lib.get(b"cppFunction");
    Ok(cpp_func(a))
}

And call the following gave me an error:

   fill_struct(&mut s);

Expected Raw pointer, found reference.

Anything I am missing ?

Thank you

there are countless things could cause a ffi call to crash. that's why ffi is always unsafe. most common reasons including incorrect data structure layout or incorrect function type declaration. it's your responsibility to make sure the foreign types are declared correctly.

in your case, I suspect you forget the null terminator of the symbol name to load. also, the symbol type should be unsafe extern "C" fn(*mut A), I believe if you omit the "C" part, it default to "Rust" ABI.

Thank you nerditation for your reply. I have ensured the following:

  1. The functions are marked as extern "C"
  2. The loading code as as follows:
let cpp_func: libloading::Symbol<unsafe extern fn(*mut A)> = lib.get(b"cppFillStructData")?;
  1. The Rust struct which is written exactly same as LegionMammal978's code.

Apart from the above points, owing to my lack of knowledge in Rust, I'm unable to figure out why FFI is crashing.

Any help would be highly appreciated.

Thank you

did you read my comment carefully? you didn't include a null terminator in the symbol name

3 Likes

What is the exact error you are getting from the compiler? Normally, references should be able to implicitly coerce into raw pointers.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.