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

Zac*_*aro 3 python ctypes

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

有没有办法加载.h带有ctypes 的文件并访问所有常量?

aba*_*ert 5

没有.

早期版本ctypes附带一个名为的模块codegenerator,它将解析头文件,既可以获取常量值,也可以将原型转换为restype/ argtypes声明.但是,据我所知,这从未完成,并且在包含在stdlib之前从包中删除.

您可以深入挖掘源代码并提取常量,同时跳过更复杂的原型.


但是,我通常这样做的方法是编写自己的生成器.

例如,在安装过程中运行此脚本:

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))
Run Code Online (Sandbox Code Playgroud)

然后foo.py可以from _foo_h import *.

为此写一个完美的正则表达式是非常困难的,也许是不可能的; 编写一个适用于您在给定项目中实际关注的标题的标题非常简单.实际上,通常,无论是上面的那个,还是跳过评论的,都是你需要的.

但有时这不起作用.例如,头文件可能#define FOO_SIZE 8用于64位构建,和#define FOO_SIZE 4用于32位构建.你怎么处理?

为此,您要求编译器为您执行此操作.大多数编译器都有一种方法可以对文件进行预处理,以获得所有活动定义.有些编译器甚至可以以漂亮的格式转储宏定义,跳过其他所有内容.随着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
Run Code Online (Sandbox Code Playgroud)

您可能希望传入一些额外的编译器标志来控制适当定义的内容,如结果 pkgconfig foo --cflags.

这也将为您提供在foo.h(递归)包含的任何内容中定义的宏,以及gcc的内置宏.您可能想要也可能不想要这些.在69105 gcc旗帜的某处,我相信有办法控制它,但我不记得了.


请注意,这些都不会为您提供常量变量或枚举,例如:

static const int SPAM_SPAM_SPAM = 73;
enum {
    kSPAM = 1,
    kEGGS
};
Run Code Online (Sandbox Code Playgroud)

解析变得更加困难; 你想要使用像pycparser-or 这样的真正的C99解析器,或者,你想要解析类似的输出gccxml而不是gcc -E.但即使这样也不会告诉你,kEGGS如果你没有写一点逻辑,那就是2.

如果你想处理C++,那就更糟了,constexpr和静态类成员以及用户定义的文字......


或者......你必须使用ctypes

CFFI 提供了一种从Python调用C代码的不同方法 - 它使这更容易.

Cython 让你编写几乎可以编译成C的Python代码,编译成Python扩展模块,它可以直接包含头文件.

还有各种绑定生成器(例如,SWIG)或绑定写入库(例如,boost :: python),可以通过扩展模块更轻松地将值导出到Python.