在 Python cffi 中的库之间传递对象

Pass objects between libraries in Python cffi

如果我使用 cffi.FFI.new 创建一个新结构,如何将其传递给具有相同结构定义的不同 FFI 的函数?

我有一个基本的 C 结构,我在 Python 中通过 cffi 包使用它,我想将其传递给 cffi 在运行时生成和编译的各种函数。但是,我不知道如何让生成的函数共享相同的结构定义,以便我可以在它们之间传递对象。 cffi 不喜欢用一个 FFI 构建对象并将其传递给另一个 FFI.

的函数

这是结构定义和在 Python 中创建实例的简化可运行示例:

from cffi import FFI

common_header = """
typedef struct {
  int32_t a;
  double b;
} my_struct;
"""

# FFI for building objects
ffibuilder = FFI()
ffibuilder.cdef(common_header)

# Build an object in Python
my_object = ffibuilder.new('my_struct*')
my_object.a = 3
my_object.b = 2.0

我有一个外部库,它生成函数的源代码,这些函数采用指向该结构实例的指针。我目前使用 CFFI 的 API 模式编译它们。这里重要的是,函数可能是在对象构造完成后生成的,所以我不能简单地提前将所有函数收集在一起并编译为一个库。

# Builder for functions generated at runtime
def build_library(header: str, source: str):
    from tempfile import TemporaryDirectory

    ffitemp = FFI()

    ffitemp.cdef(common_header + header)

    ffitemp.set_source('_temp', source)

    with TemporaryDirectory() as temp_dir:
        lib_path = ffitemp.compile(tmpdir=temp_dir)

        lib = ffitemp.dlopen(lib_path)

    return lib.func


# Use function
header = """
int func(my_struct *A);
"""

source = """
typedef struct {
  int32_t a;
  double b;
} my_struct;

int func(my_struct *A) {
    return A -> a;
}
"""

func = build_library(header, source)

当我尝试将我的结构实例传递给函数时,我收到一条错误消息,指出我传递的结构与函数接受的结构类型不同。

# Use function
a = func(my_object)
print(a)

TypeError: initializer for ctype 'my_struct *' appears indeed to be 
'my_struct *', the types are different (check that you are not e.g. 
mixing up different ffi instances)

错误很清楚为什么不开心。它不喜欢我使用 ffibuilder 构造 my_object 并将其传递给在不同的 FFI 中定义的函数,它有自己的 my_struct 类型定义。

如何编译生成的函数以与中央 FFI 共享结构定义?

您可以使用 FFI.include 将一个 FFI 实例的来源和定义包含在另一个实例中。使用包含的 FFI 构造的 Objects 可传递给包含它的 FFI 中的函数。

请注意,包含的定义不能在以后的 FFI 中重复。此外,只有在调用 set_source 时才能包含 FFI。即使您想要的只是 header,也是如此;在这种情况下,只需将源设置为空字符串即可。

这里是在main上设置空源FFI:

from cffi import FFI

common_header = """
typedef struct {
  int32_t a;
  double b;
} my_struct;
"""

# FFI for building objects
ffibuilder = FFI()
ffibuilder.cdef(common_header)
ffibuilder.set_source('_main', '')  # <-- Set empty source

这里包括叶 FFI 中的主要 FFI:

# Builder for functions generated at runtime
def build_library(header: str, source: str):
    from tempfile import TemporaryDirectory

    ffitemp = FFI()

    ffitemp.include(ffibuilder)  # <-- include main FFI

    ffitemp.cdef(header)

    ffitemp.set_source('_temp', source)

    with TemporaryDirectory() as temp_dir:
        lib_path = ffitemp.compile(tmpdir=temp_dir)

        lib = ffitemp.dlopen(lib_path)

    return lib.func