Trampolines, type of method, closure default? reducing boilerplate for FFI

I'm writing a rust library that I hope will let users easily create plugins for a program that exposes a C API.
One of the things that this API allows for is creating classes with named methods with various arguments.

I end up writing trampolines that I give the the API that then calls back into my code, does some casting, and calls a method. Something like the below.

pub struct A;

impl A {
    pub fn x(&mut self, v: i64) {
        println!("x {}", v);
    }
    pub fn y(&mut self, v: i64) {
        println!("y {}", v);
    }
}

pub unsafe extern "C" fn x_tramp(o: *mut A, v: i64) {
    let o = &mut *o;
    o.x(v);
}

pub unsafe extern "C" fn y_tramp(o: *mut A, v: i64) {
    let o = &mut *o;
    o.y(v);
}

While I know I can write macros to write these trampolines for me, I'm wondering if there is a way to use generics to make reduce the need for macros (and not require users of the library to generate trampolines)?

Is there a way to encode call method y on o with v in a type so I can make a generic trampoline that executes that?

Since a closure has a unique type, can we do something with that and not a specific one for each of the methods x and y?

Something like..

pub unsafe extern "C" fn int_tramp<T, F: Fn(&mut T, i64) + Default>(o: *mut T, v: i64) {
    let f: F = Default::default();
    let o = &mut *o;
    f(o, v);
}

Any ideas?

Well this has same effect with your trampolines.

pub struct A;

impl A {
    #[export_name = "x_tramp"]
    pub extern "C" fn x(&mut self, v: i64) {
        println!("x {}", v);
    }
    #[export_name = "y_tramp"]
    pub extern "C" fn y(&mut self, v: i64) {
        println!("y {}", v);
    }
}

ahh, and could I just use A::y instead of y_tramp to the FFI in this case? As long as the FFI gets the correct, mangled name?
I was hoping to not have to expose the extern "C" to the library users.. but I guess it isn't that big of a deal.

I swear someone told me that I shouldn't pass (&mut self) where the FFI was expecting (*mut self) but maybe I'm a remembering wrong.

Now that I've thought about it some more I'm remembering a key detail.

The trampolines the library users need to write don't actually just call their methods.
There is a wrapper that is actually registered with the C API, and the trampoline gets a wrapper object and then calls a method on the wrapped object.

The playground below shows 4 example calls, 2 with what I'd ideally have and 2 with the trampoline I think I need.

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.