47个版本 (12个破坏性版本)

0.13.8 2021年11月8日
0.12.0 2021年10月4日
0.5.1 2021年7月30日

#4#cffi

Download history 8/week @ 2024-03-14 107/week @ 2024-03-28 11/week @ 2024-04-04

每月114次下载

MIT 许可证

230KB
3.5K SLoC

Interoptopus 生成CPython CFFI绑定。

⚠️ 弃用通知

此后端已被弃用。请使用 interoptopus_backend_cpython 代替,它具有更好的Python兼容性并生成更干净的代码。

使用方法

假设您已经编写了一个包含您的FFI逻辑的crate,名为 example_library_ffi,并希望为Python 3.7+生成 CPython CFFI绑定,请按照以下说明操作。

在您的库中

Interoptopus 属性添加到您编写的库中,并定义一个 inventory 函数,列出您希望导出的所有符号。所有支持的结构概述可以在 参考项目 中找到。

use interoptopus::{ffi_function, ffi_type};

#[ffi_type]
#[repr(C)]
pub struct Vec2 {
    pub x: f32,
    pub y: f32,
}

#[ffi_function]
#[no_mangle]
pub extern "C" fn my_function(input: Vec2) -> Vec2 {
    input
}

interoptopus::inventory!(my_inventory, [], [my_function], [], []);

将这些添加到您的 Cargo.toml 中,以便可以找到属性和绑定生成器(用最新的版本号替换 ...

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
interoptopus = "..."
interoptopus_backend_cpython_cffi = "..."

tests/bindings.rs 中创建一个单元测试,该测试在运行时将生成您的绑定,使用 cargo test。在实际项目中,您可能希望将此代码添加到另一个crate中

use interoptopus::util::NamespaceMappings;
use interoptopus::{Error, Interop};

#[test]
fn bindings_cpython_cffi() -> Result<(), Error> {
    use interoptopus_backend_cpython_cffi::{Config, Generator};

    let library = example_library_ffi::my_inventory();

    Generator::new(Config::default(), library)
        .write_file("bindings/python/example_library.py")?;

    Ok(())
}

现在运行 cargo test

如果任何内容不清楚,您可以在 Github上的工作示例 中找到。

生成的输出

以下输出是此后端可能生成的。如果您想自定义某些内容,请查看 Config 结构体。如果您真的不喜欢某些内容的生成方式,可以轻松地 创建自己的后端

from cffi import FFI

api_definition = """
typedef struct cffi_vec2
    {
    float x;
    float y;
    } cffi_vec2;


cffi_vec2 my_function(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 my_function(input):
        """ Function using the type."""
        global _api
        if hasattr(input, "_ctx"):
            input = input._ctx[0]

        return _api.my_function(input)

依赖项