有没有办法通过ctypes加载存储在头文件中的常量值?

时间:2013-09-12 22:35:02

标签: python ctypes

我正在对底层操作系统库进行一系列ctypes调用。只要文档引用存储在.h中的常量值,我的进度就会变慢。文件在哪里,因为我必须追踪它,并弄清楚实际值是什么,以便我可以将它传递给函数。

有没有办法用ctypes加载.h文件并获取所有常量的访问权限?

1 个答案:

答案 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和与标志兼容的编译器,如gccclang预处理和-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。