You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+15-18Lines changed: 15 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,42 +4,39 @@ A tool for separating the implementation of FFI interfaces from language-specifi
4
4
5
5
## Problem
6
6
7
-
Making FFI (Foreing Function Interface) for Rust library is not an easy task. This involves making great amount of boilerplate code which wraps Rust API to`extern "C"` functions and `#[repr(C)]` structures.
8
-
It's already hard for a single lagnuage, but when it's necessary to add more languages, situation complexifies.
7
+
Making FFI (Foreign Function Interface) for a Rust library is not an easy task. This involves a large amount of boilerplate code that wraps the Rust API in`extern "C"` functions and `#[repr(C)]` structures.
8
+
It's already hard for a single language, but when you need to add more languages, the situation becomes more complex.
9
9
10
-
The root cause of this complexity is that in multi-language case is that we should either:
10
+
The root cause of this complexity in a multi-language scenario is that you must either:
11
11
12
-
- Make separate crates for each language FFI. E.g we have Rust library "foo" and cdylib/staiclib crates "foo-c", "foo-csharp", "foo-java", etc. Each crate contains it's own independent repr-C wrappers. Code duplication is huge.
13
-
- Generate all language-specific libraries from the same source. In this case we just replacing code duplication with code complexity: the single FFI library should conform all language targets.
12
+
- Make separate crates for each language's FFI. For example, you have a Rust library "foo" and cdylib/staticlib crates "foo-c", "foo-csharp", "foo-java", etc. Each crate contains its own independent wrappers for "foo". Code duplication is huge.
13
+
- Generate all language-specific libraries from the same source. In this case, you just replace code duplication with code complexity: the single FFI library must conform to all language targets.
14
14
15
-
One little example: the `cbindgen` supports wrapper types (`Option`, `MaybeUninit`) in the `extern "C"` functions. But the `csbindgen` (binding generator for C#) don't understand them. The following FFI function is good for C but can't be used for C#
15
+
A small example: `cbindgen` supports wrapper types (`Option`, `MaybeUninit`) in `extern "C"` functions, but `csbindgen` (a binding generator for C#) doesn't understand them. The following FFI function works for C but can't be used for C#:
And there are more such quirks which makes hard to support common source for multiple languages.
22
+
And there are more such quirks, which make it hard to support a common source for multiple languages.
26
23
27
24
## Solution
28
25
29
-
The proposed solution is to make common Rust library (e.g. "foo-ffi") which wraps original "foo" library to FFI-compatible functions, but do not add `extern "C"` and `#[no_mangle]` modifiers to them. Instead it marks these funcions with `#[prebindgen]` macro.
26
+
The proposed solution is to create a common Rust library (e.g., "foo-ffi") that wraps the original "foo" library in FFI-compatible functions, but does not add `extern "C"` and `#[no_mangle]` modifiers. Instead, it marks these functions with the`#[prebindgen]` macro.
The dependent language-specific crates ("foo-c", "foo-cs", etc) in this case contains only autogenerated code based on these marked functions, with added necessary `extern "C"` and `#[no_mangle]`, stipped out wrapper types, etc.
33
+
The dependent language-specific crates ("foo-c", "foo-cs", etc.) in this case contain only autogenerated code based on these marked functions, with the necessary `extern "C"` and `#[no_mangle]` added, stripped-out wrapper types, etc.
37
34
38
35
### Architecture
39
36
40
-
Each element to export is marked in the source crate with the `#[prebindgen]` macro. When the source crate is compiled, these elements are written to an output directory. The `build.rs` of the destination crate reads these elements and creates FFI-compatible functions and proxy structures for them. The generated source file is included with the `include!()` macro in the dependent crate and parsed by the language binding generator (e.g., cbindgen).
37
+
Each element to be exported is marked in the source crate with the `#[prebindgen]` macro. When the source crate is compiled, these elements are written to an output directory. The destination crate's `build.rs` reads these elements and creates FFI-compatible functions and proxy structures for them. The generated source file is included with the `include!()` macro in the dependent crate and parsed by the language binding generator (e.g., cbindgen).
41
38
42
-
It's important to keep in mind that `[build-dependencies]` and `[dependencies]` are different. The `#[prebindgen]` macro collects sources when compiling the `[build-dependencies]` instance of the source crate. Later, these sources are used to generate proxy calls to the `[dependencies]` instance, which may be built with a different feature set and for a different architecture. A set of assertions is put into the generated code to catch possible divergences, but it's the developer's job to manually resolve these errors.
39
+
It's important to keep in mind that `[build-dependencies]` and `[dependencies]` are different. The `#[prebindgen]` macro collects sources when compiling the `[build-dependencies]` instance of the source crate. Later, these sources are used to generate proxy calls to the `[dependencies]` instance, which may be built with a different feature set and for a different architecture. A set of assertions is added to the generated code to catch possible divergences, but it's the developer's job to manually resolve these errors.
43
40
44
41
## Usage
45
42
@@ -51,12 +48,12 @@ Mark structures and functions that are part of the FFI interface with the `prebi
51
48
// example-ffi/src/lib.rs
52
49
useprebindgen_proc_macro::prebindgen;
53
50
54
-
// Path to prebindgen output directory. The `build.rs` of the destination crate
51
+
// Path to the prebindgen output directory; the destination crate's `build.rs`
0 commit comments