1

I have a library I need to use, which exposes a method that sets some callback. But that method takes a void*. That is later recasted into a void(*)(int) (funtion that takes an int).

Something like:

#include "stdio.h"

typedef void (*callback)(int i);

void call_it(void* f) {
    callback fn = (callback) f;
    fn(10);
}

Then, I need to pass that callback from rust:

use std::ffi::c_void;

#[link(name = "demo", kind = "static")]
extern "C" {
    fn call_it(f: *mut c_void);
}

fn p(n: isize) {
    println!("{n}");
}

fn run<F: FnMut(isize) + 'static>(mut f: F) {
    let mut cb: &mut dyn FnMut(isize) = &mut f;
    let ptr = &mut cb as *mut &mut _ as *mut c_void;
    unsafe { call_it(ptr) };
}

fn main() {
    run(p);
}

Which runs into a SEGFAULT Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

I have already checked some questions/answers, like:

How do I create a Rust callback function to pass to a FFI function?

How do I convert a Rust closure to a C-style callback?

Calling Rust function with function parameter from C results in SegFault

And couldn't get my head around it.

I cannot change the C part, so what can I do from the rust side?

Herohtar
  • 5,347
  • 4
  • 31
  • 41
Netwave
  • 40,134
  • 6
  • 50
  • 93
  • Casting between `void*` and function pointers isn't well-defined behavior. `void*` is only a generic type for object pointers. You should rethink the C design. – Lundin Sep 29 '22 at 14:46
  • 1
    @Lundin That may be, but there are lots of poorly-written C libraries that do so regardless, and my guess is that OP is not the one writing the C library in question. – Silvio Mayolo Sep 29 '22 at 14:55
  • Exactly, sadly I cannot modify the C part...otherwise there would not be a C part to begin with :D – Netwave Sep 29 '22 at 15:02

1 Answers1

3

You may consider follow the example Callbacks from C code to Rust functions.

The requirement for this is that the callback function is marked as extern with the correct calling convention to make it callable from C code.

Just turn your Rust code into:

use std::ffi::c_void;

#[link(name = "demo", kind = "static")]
extern "C" {
    fn call_it(f: *mut c_void);
}

extern "C" fn p(n: isize) {
    println!("{n}");
}

fn run(f: extern "C" fn(isize)) {
    unsafe { call_it(f as *mut c_void) };
}

fn main() {
    run(p);
}

Field
  • 389
  • 1
  • 4