Generates CPython CFFI bindings for Interoptopus.
Assuming you have written a crate containing your FFI logic called example_library_ffi
and
want to generate CPython CFFI bindings for Python 3.7+, follow the instructions below.
Add Interoptopus attributes to the library you have written, and define an inventory function listing all symbols you wish to export. An overview of all supported constructs can be found in the reference project.
```rust use interoptopus::{ffifunction, ffitype};
pub struct Vec2 { pub x: f32, pub y: f32, }
pub extern "C" fn my_function(input: Vec2) -> Vec2 { input }
interoptopus::inventory!(myinventory, [], [myfunction], [], []); ```
Add these to your Cargo.toml
so the attributes and the binding generator can be found
(replace ...
with the latest version):
```toml [lib] crate-type = ["cdylib", "rlib"]
[dependencies] interoptopus = "..." interoptopusbackendcpython_cffi = "..." ```
Create a unit test in tests/bindings.rs
which will generate your bindings when run
with cargo test
. In real projects you might want to add this code to another crate instead:
```rust use interoptopus::util::NamespaceMappings; use interoptopus::{Error, Interop};
fn bindingscpythoncffi() -> Result<(), Error> { use interoptopusbackendcpython_cffi::{Config, Generator};
let library = example_library_ffi::my_inventory();
Generator::new(Config::default(), library)
.write_file("bindings/python/example_library.py")?;
Ok(())
} ```
Now run cargo test
.
If anything is unclear you can find a working sample on Github.
The output below is what this backend might generate. Have a look at the [Config
] struct
if you want to customize something. If you really don't like how something is generated it is
easy to create your own.
```python from cffi import FFI
apidefinition = """ typedef struct cffivec2 { float x; float y; } cffi_vec2;
cffivec2 myfunction(cffi_vec2 input); """
ffi = FFI() ffi.cdef(api_definition) _api = None
def init_api(dll): """Initializes this library, call with path to DLL.""" global _api _api = ffi.dlopen(dll)
class Vec2(object): """ A simple type in our FFI layer.""" def init(self): global api, ffi self.ctx = ffi.new("cffi_vec2[]", 1)
def array(n):
global _api, ffi
return ffi.new("cffi_vec2[]", n)
def ptr(self):
return self._ctx
@property
def x(self):
""""""
return self._ctx[0].x
@x.setter
def x(self, value):
self._ptr_x = value
self._ctx[0].x = value
@property
def y(self):
""""""
return self._ctx[0].y
@y.setter
def y(self, value):
self._ptr_y = value
self._ctx[0].y = value
class raw: """Raw access to all exported functions.""" def myfunction(input): """ Function using the type.""" global _api if hasattr(input, "ctx"): input = input._ctx[0]
return _api.my_function(input)
```