使用 Python 的 CFFI 并排除系统 headers
Using Python's CFFI and excluding system headers
我正在尝试使用 Python's CFFI 开发 Python 绑定到用 C 编写的科学模型。CFFI 文档有点稀疏,我被困在 cdef
阶段。
到目前为止,我的流程遵循以下步骤:
预处理 header 个文件:
gcc -E -gcc -std=c99 -E -P src/my_c_interface.c -I./include/ -I../shared/include/ > header.txt
这会生成一个文本文件,其中包含我的 include/
目录中的 header 文件中包含的所有 C 声明。它还包括标准库的声明(我很确定这就是我的问题所在)。 header.txt
看起来像这样(完整的 header.txt 是 here):
从系统开始 header 东西:
typedef float float_t;
typedef double double_t;
extern int __math_errhandling(void);
extern int __fpclassifyf(float);
extern int __fpclassifyd(double);
extern int __fpclassifyl(long double);
extern __inline __attribute__((__gnu_inline__)) __attribute__ ((__always_inline__)) int __inline_isfinitef(float);
extern __inline __attribute__((__gnu_inline__)) __attribute__
并以我的 header 中定义的部分结束:
FILE *LOG_DEST;
void finalize_logging(void);
void get_current_datetime(char *cdt);
void get_logname(const char *path, int id, char *filename);
使用cffi
解析预处理后的header个文件:
import cffi
ffi = cffi.FFI()
with open('drivers/python/header.txt') as f_headers:
ffi.cdef(f_headers.read()) # error is raised here
ffi.compile()
这 returns 以下错误(完整回溯是 here):
/Users/me/anaconda/lib/python3.4/site-packages/cffi/cparser.py in convert_pycparser_error(self, e, csource)
157 else:
158 msg = 'parse error\n%s' % (msg,)
--> 159 raise api.CDefError(msg)
160
161 def parse(self, csource, override=False, packed=False):
CDefError: cannot parse "extern __inline __attribute__((__gnu_inline__)) __attribute__ ((__always_inline__)) int __inline_isfinitef(float);"
:10:17: before: __attribute_
鉴于我所处的位置,我有几个问题要问那些比我更熟悉 cffi 的人:
- 是否可以让预处理器排除系统 headers?
- 是否有人知道比
cffi
文档中显示的示例更复杂的示例?现实世界的例子会有所帮助。
- 看看我上面显示的例子,我是不是漏掉了什么重要的东西?
这是一个有点笼统的回答:
虽然可以使用 gcc -E
方法并手动 "trim" 结果,但这不是使用 CFFI 的推荐方法。相反,cdef() 代码通常是增量(根据需要添加函数)或从 .h 文件的编辑副本批量生成的。从手册页复制时,第一种方法效果最好;第二种方法适用于我们想要完全访问单个第 3 方库的情况。
在所有情况下,很可能您仍然需要编辑 .h 文件:推荐的方法是使用 ffi.set_source(),并从 cdef() 中删除任何多余的声明,将它们替换为 ...
。例如,实际的 .h 文件可能包含声明 #define FOOBAR 42
,但不应依赖值 42(例如,它将来可能会更改),因此 cdef() 应该接收 #define FOOBAR ...
.
我正在尝试使用 Python's CFFI 开发 Python 绑定到用 C 编写的科学模型。CFFI 文档有点稀疏,我被困在 cdef
阶段。
到目前为止,我的流程遵循以下步骤:
预处理 header 个文件:
gcc -E -gcc -std=c99 -E -P src/my_c_interface.c -I./include/ -I../shared/include/ > header.txt
这会生成一个文本文件,其中包含我的
include/
目录中的 header 文件中包含的所有 C 声明。它还包括标准库的声明(我很确定这就是我的问题所在)。header.txt
看起来像这样(完整的 header.txt 是 here):从系统开始 header 东西:
typedef float float_t; typedef double double_t; extern int __math_errhandling(void); extern int __fpclassifyf(float); extern int __fpclassifyd(double); extern int __fpclassifyl(long double); extern __inline __attribute__((__gnu_inline__)) __attribute__ ((__always_inline__)) int __inline_isfinitef(float); extern __inline __attribute__((__gnu_inline__)) __attribute__
并以我的 header 中定义的部分结束:
FILE *LOG_DEST; void finalize_logging(void); void get_current_datetime(char *cdt); void get_logname(const char *path, int id, char *filename);
使用
cffi
解析预处理后的header个文件:import cffi ffi = cffi.FFI() with open('drivers/python/header.txt') as f_headers: ffi.cdef(f_headers.read()) # error is raised here ffi.compile()
这 returns 以下错误(完整回溯是 here):
/Users/me/anaconda/lib/python3.4/site-packages/cffi/cparser.py in convert_pycparser_error(self, e, csource) 157 else: 158 msg = 'parse error\n%s' % (msg,) --> 159 raise api.CDefError(msg) 160 161 def parse(self, csource, override=False, packed=False): CDefError: cannot parse "extern __inline __attribute__((__gnu_inline__)) __attribute__ ((__always_inline__)) int __inline_isfinitef(float);" :10:17: before: __attribute_
鉴于我所处的位置,我有几个问题要问那些比我更熟悉 cffi 的人:
- 是否可以让预处理器排除系统 headers?
- 是否有人知道比
cffi
文档中显示的示例更复杂的示例?现实世界的例子会有所帮助。 - 看看我上面显示的例子,我是不是漏掉了什么重要的东西?
这是一个有点笼统的回答:
虽然可以使用 gcc -E
方法并手动 "trim" 结果,但这不是使用 CFFI 的推荐方法。相反,cdef() 代码通常是增量(根据需要添加函数)或从 .h 文件的编辑副本批量生成的。从手册页复制时,第一种方法效果最好;第二种方法适用于我们想要完全访问单个第 3 方库的情况。
在所有情况下,很可能您仍然需要编辑 .h 文件:推荐的方法是使用 ffi.set_source(),并从 cdef() 中删除任何多余的声明,将它们替换为 ...
。例如,实际的 .h 文件可能包含声明 #define FOOBAR 42
,但不应依赖值 42(例如,它将来可能会更改),因此 cdef() 应该接收 #define FOOBAR ...
.