我正在对底层操作系统库进行一系列ctypes调用。只要文档引用存储在.h
中的常量值,我的进度就会变慢。文件在哪里,因为我必须追踪它,并弄清楚实际值是什么,以便我可以将它传递给函数。
有没有办法用ctypes加载.h
文件并获取所有常量的访问权限?
答案 0 :(得分:5)
没有
ctypes
的早期版本附带了一个名为codegenerator
的模块,该模块将解析头文件,以获取常量值并将原型转换为restype
/ {{1}声明。但是,据我所知,这从未完成,并且在包含在stdlib中之前已从包中删除。
你可以深入研究the source并提取常数,同时跳过更复杂的原型。
然而,我通常采用的方法是编写自己的生成器。
例如,在安装过程中运行此脚本:
argtypes
然后constants = {}
with open('foo.h') as infile:
for name, value in re.findall(r'#define\s+(\w+)\s+(.*)', infile):
try:
constants[name] = ast.literal_eval(value)
except Exception as e:
pass # maybe log something
with open('_foo_h.py', w) as outfile:
outfile.write(repr(constants))
只能foo.py
。
为此写一个完美的正则表达式是非常困难的,也许是不可能的;编写一个适用于您在给定项目中实际关注的标题的标题非常简单。事实上,通常,无论是上面的那个,还是跳过评论的,都是你需要的。
但有时这不起作用。例如,对于64位版本,头文件可能为from _foo_h import *
,对于32位版本,头文件可能为#define FOO_SIZE 8
。你是如何处理的?
为此,您要求编译器为您执行此操作。大多数编译器都有一种方法可以对文件进行预处理,以获得所有活动定义。有些编译器甚至可以以漂亮的格式转储宏定义,跳过其他所有内容。使用#define FOO_SIZE 4
和与标志兼容的编译器,如gcc
,clang
预处理和-E
转储宏。所以:
-dM
您可能希望传入一些额外的编译器标志来控制适当定义的内容,例如macros = subprocess.check_output(['gcc', '-dM', '-E', '-', 'foo.h'])
for line in macros.splitlines():
try:
_, name, value = line.split(None, 2)
constants[name] = ast.literal_eval(value)
except Exception as e:
pass # again, do something nicer
的结果。
这也将为您提供在pkgconfig foo --cflags
(递归)包含的任何内容中定义的宏,以及gcc的内置宏。您可能想要也可能不想要这些。在the 69105 gcc flags之间的某个地方,我相信有办法控制它,但我不记得了。
请注意,这些都不会为您提供常量变量或枚举,例如:
foo.h
解析变得更加困难;你想要使用真正的C99解析器,如pycparser
- 或者,你想要解析static const int SPAM_SPAM_SPAM = 73;
enum {
kSPAM = 1,
kEGGS
};
而不是gccxml
之类的输出。但即便如此,如果没有你写一点逻辑,gcc -E
就不会告诉你。
如果你想处理C ++,那就更糟了,constexpr和静态类成员以及用户定义的文字......
或者......你必须使用kEGGS
吗?
CFFI
提供了一种从Python调用C代码的不同方法 - 它使这更容易。
Cython
允许您编写几乎可以编译为C的Python代码,并将其编译为Python扩展模块,并且可以直接包含头文件。
还有各种绑定生成器(例如,SWIG)或绑定写入库(例如,boost :: python),可以通过扩展模块更轻松地将值导出到Python。