简体   繁体   English

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

[英]Pass objects between libraries in Python cffi

If I make a new struct with cffi.FFI.new , how do I pass it to a function from a different FFI that has the same struct definition? 如果使用cffi.FFI.new创建新结构,如何将其传递给具有相同结构定义的其他FFI中的函数?

I have a basic C struct that I am using in Python via the cffi package that I want to pass to various functions generated and compiled by cffi at runtime. 我有一个基本的C结构,正在通过cffi包在Python中使用,我想在运行时传递给cffi生成和编译的各种函数。 However, I do not know how to get the generated functions to share the same struct definition so that I can pass objects between them. 但是,我不知道如何获取生成的函数以共享相同的结构定义,以便我可以在它们之间传递对象。 cffi does not like it when building an object with one FFI and passing it to a function from another FFI . 当使用一个FFI构建对象并将其从另一个FFI传递给函数时,cffi不喜欢它。

Here is a simplified runnable example of the struct definition and creating an instance in Python: 这是结构定义和在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

I have an external library that generates the source code of functions that take pointers to instances of this struct. 我有一个外部库,该库生成函数的源代码,这些函数采用指向此结构实例的指针。 I currently compile them using the API-mode of CFFI. 我目前使用CFFI的API模式进行编译。 The important thing here is that the functions may be generated after the objects have been constructed, so I cannot simply collect all the functions together ahead of time and compile them as one library. 这里重要的是,这些函数可以在构造对象之后生成,因此我不能简单地提前将所有函数收集在一起并将它们编译为一个库。

# 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)

When I try to pass instances of my struct to the function, I get an error saying that the struct I am passing in is not the same type as the one accepted by the function. 当我尝试将结构的实例传递给函数时,我收到一条错误消息,提示我传递的结构与函数接受的结构类型不同。

# 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)

The error is pretty clear about why it is unhappy. 该错误非常清楚为什么不满意。 It does not like that I have constructed my_object using ffibuilder and passed it to a function defined in a different FFI , which has its own definition of the my_struct type. 它不喜欢我使用ffibuilder构造my_object并将其传递给另一个FFI定义的函数,而FFI具有自己的my_struct类型定义。

How do I get the compilation of the generated functions to share a struct definition with a central FFI? 如何获得所生成函数的编译结果以与中央FFI共享结构定义?

You can use FFI.include to include the source and definitions of one FFI instance in another. 您可以使用FFI.include将一个FFI实例的源和定义包含在另一个FFI实例中。 Objects constructed with the included FFI are passable to functions in the FFI in which it was included. 使用包含的FFI构造的对象可传递到包含它的FFI中的功能。

Note that an included definition cannot be duplicated in later FFI . 注意,包含的定义不能在以后的FFI重复。 Also, an FFI can only be included if set_source has been called on it. 同样,只有在已调用set_source下,才能包括FFI This is true even if all you want is the header; 即使您只需要标头,也是如此。 in which case, simply set the source to an empty string. 在这种情况下,只需将源设置为空字符串。

Here is setting the empty source on the main FFI : 这是在主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

And here is including the main FFI in the leaf FFI : 这里是包括主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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM