我正在尝试使用Python cffi库实例化一个struct。我想从我自己的.h文件以及标准库中的结构实例化一个结构。
import datetime
import os
from cffi import FFI
clib = None
script_path = os.path.dirname(os.path.realpath(__file__))
ffi = FFI()
with open(os.path.join(script_path, 'myheader.h'), 'r') as myfile:
source = myfile.read()
ffi.cdef(source)
clib = ffi.dlopen('mylib')
# these all fail
ffi.new("struct tm")
ffi.new("struct tm[]", 1)
ffi.new("struct tm *")
ffi.new("struct mystruct")
答案 0 :(得分:1)
ffi.new("struct mystruct")
不正确,您的意思可能是ffi.new("struct mystruct *")
。
struct tm
很可能未在cdef()
中定义,即在您的情况下,myheader.h
内未提及。您需要先在cdef()
中定义它,然后才能使用它,即使它位于一个通用的标准标题中。
您可能最好使用set_source()
(API模式),因为您可以使用struct tm
的近似定义,例如类似的东西:
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
...; /* literally a "..." here */
};
如果您使用dlopen()
(ABI模式),则必须使用与平台标头中完全相同的声明。结果不太便携。
答案 1 :(得分:0)
TL; DR Armin可能是正确的(并且绝对是此处的专家)。如果tm
是ctime的结构,则需要对其进行定义。如果它是您自己的结构,但未在myheader.h
中定义,则需要添加定义或将相关标头读取到source
字符串中。如果其他所有方法均失败,则可能需要typedef
。
这行不通:
ffi.new("struct tm")
这应该有效:
ffi.new("struct tm[]", 1)
但是这个看起来更好:
ffi.new("struct tm *")
但是,这对我来说没有意义:
ffi.new("struct mystruct")
如果将tm
定义为typedef,则不需要struct
。如果将ctime tm重新定义为mystruct,则此处需要“ *”。如果它是typedef,则不能指定struct,但是可以使用它:
ffi.new("mystruct *")
我无法使用不是typedef
的结构。如果Armin没有其他暗示,我会声明不支持它们。也许您遇到了同样的问题。
如果您对头文件和结构名称有任何控制权或话语权,无论如何,我强烈建议对结构进行typedef定义。这是C语言中结构的通用做法。
typedef struct tm {
/* struct internals here */
} tm; // not recommended using this short of a name though
除非tm
是代码的完全核心,以至于其含义总是显而易见的,强烈建议在使用typedef时使用更具描述性的名称。这成为全局定义,因此也有助于避免发生任何冲突,例如ctime
结构tm
。这样会更好:
typedef struct {
/* struct internals here */
} taskmaster; // or something similarly descriptive, having nothing else to go on I assume 'tm' is referring to my favorite UK panel show and not ctime
您也可以像在这里一样完全丢弃tm
。使用typedef时仅需要最终类型名称。
无论如何,关键是typedef
您不要在声明中使用struct
:
mytm = ffi.new("tm *") # again, this level of abbreviation is not recommended
或
mytm = ffi.new("taskmaster *")
还请记住,CFFI不理解指令,例如#include。因此,如果myheader.h
没有定义struct tm
而是将其从另一个文件(例如ctime)中拉出,则您需要专门定义它,或者(读取并)将所有感兴趣的头文件添加到{ {1}}字符串用于cdef调用。建议不要 阅读标准的lib标头。