Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Generate rust fn and struct declarations instead of C headers #204

Open
coderedart opened this issue Feb 4, 2024 · 3 comments
Labels
K-feature Kind: proposed new code to implement new behaviour

Comments

@coderedart
Copy link

coderedart commented Feb 4, 2024

My use case is pretty simple. I need to do rust <-> rust FFI for webassembly.

Basically, i want my app to be "scriptable" using wasm plugins. So, i would provide a bunch of functions (like drawing) from the host. When the guest plugin is loaded, it will be linked with these host functions as "imports". Then, the guest can call the host functions to draw stuff.

So, the host will have code like this:

// host.rs
struct Point {
	x: f32,
	y: f32
}

impl Point {
	/// Draw a line from self -> to
	pub fn draw_line(&self,  to: &Point) {
		// ... host side implementation using opengl or something.
	}
} 

Then, there will be a "guest_imports.rs" crate which will just include the FFI declarations necessary. Basically, bindings generated from C headers.

// generated from host.rs
struct Point {
	x: f32,
	y: f32,
}
// will be provided (linked) as imports by host at load time.
extern "C" {
	pub fn point_draw_line(this: * const Point, to: * const Point);
}
impl Point {
	/// automatically reconstruct the safe API on the guest side using the FFI bindings generated from the host
	pub fn draw_line(&self, to: &Point) {
		unsafe { point_draw_line(self as * const Point, to as * const Point) }
	}
}

Now, a guest plugin can simply depend on the above crate, with almost the same API (except with dynamically lined unsafe bindings under the hood).

Right now, trying to write rust <-> rust FFI is a horrible experience.

  1. Write the host functionality.
  2. use safer_ffi to generate the extern FFI fn declarations or do it manually.
    1. If you used safer_ffi, now you need to use bindgen to convert c headers to rust.
  3. Then, reconstruct the safe API using the FFI fns. copy paste the docs too :(

This leads to a lot of duplication and manual maintenance. For some reason, its more work to do rust <-> rust FFI than rust <-> C/Cpp FFI.

But, i was wondering if it is possible for safer_ffi to instead directly generate the rust ffi declarations (and even reconstruct the safe API using the declarations) automatically and skip the intermediary c language completely.

@danielhenrymantilla danielhenrymantilla added the K-feature Kind: proposed new code to implement new behaviour label Feb 5, 2024
@danielhenrymantilla
Copy link
Collaborator

Yes, this is indeed a desired goal of this library, hopefully implemented this year (cannot promise a shorter timeframe since I don't have that much time to dedicate to this yet). For what is worth, you may be able to give ::stabby a try: it has similar design ideas to safer-ffi, but focuses on Rust-to-Rust FFI 🙂

@ivila
Copy link

ivila commented Nov 27, 2024

@coderedart I think you may want abi_stable
image

@coderedart
Copy link
Author

Nah, that's for passing ABI-stable data structures (eg: string) across FFI boundary in the same memory space. That would be useless for wasm guest <-> native host FFI, as wasm guests cannot access host memory.

All I want is for safer_ffi to reconstruct the same API on the other side using C FFI declarations. For example,

// host crate implementing the actual functionality
struct Point { x: f32, y: f32 }
impl Point {
  fn draw_line(&self, other: &Point) {
    // impl using skia or opengl etc. on native host
  }
}

// safer_ffi generates these declarations for guest crates (for C, C++ etc..)
struct Point { x: f32, y: f32 } 
extern "C" fn draw_line(s: *const Point, other: *const Point); // imported from host

// crate for rust guest crates - regenerate the same safe API (like original) wrapping FFI 
struct Point { x: f32, y: f32 }
impl Point { 
  fn draw_line(&self, other: &Point)  {
     // calling host fns linked at startup - no idea about how its implemented on host
    unsafe { draw_line(self as *const Point, other as *const Point) }
  }
}

The first and second are already done by safer_ffi. Its the last part of [re]generating the safe API on top of unsafe FFI, which would help a lot here. The host still needs to export these FFI functions, but that's out of scope here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
K-feature Kind: proposed new code to implement new behaviour
Projects
None yet
Development

No branches or pull requests

3 participants