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

时间:2019-03-04 15:36:58

标签: python python-cffi

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

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

这是结构定义和在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)

关于为什么不满意的错误非常清楚。我不喜欢使用my_object来构造ffibuilder并将其传递给在不同的FFI中定义的函数,该函数具有自己的my_struct类型定义。 / p>

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

1 个答案:

答案 0 :(得分:0)

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

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

这里是在主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