python可以从C头文件加载定义吗?

Dsc*_*oni 6 python ctypes header-files

我正在围绕 C API 编写一个 python 包装器。我有一个广泛的 API 描述,现在我正在努力实现头文件中定义的枚举。

假设我内部有一个 C API 函数myAPI.dll,它接受一个枚举作为参数,例如:

void SomeFunction(SomeEnum data)
Run Code Online (Sandbox Code Playgroud)

从头文件中,我可以看到SomeEnum如下所示:

enum SomeEnum{
    SomeValue = 1,
    SomeOtherValue = 2,
    SomeVeryStupidValue = -1
};
Run Code Online (Sandbox Code Playgroud)

在 python 中,我加载.dll类似:

myAPI = ctypes.cdll.LoadLibrary('myAPI.dll')
Run Code Online (Sandbox Code Playgroud)

现在我希望能够调用:

myAPI.SomeFunction(SomeValue)
Run Code Online (Sandbox Code Playgroud)

我知道,我可以SomeValue在 python 中定义,但是直接从头文件加载它的定义或者直接将它作为myAPI. 这可能吗?

Mar*_*nen 6

这是可能的。几年前我编写了一个工具,使用pyparsing扫描文件中的 C++enum语法。现在它是一个pyparsing 示例,我在此处复制了它,以防链接发生更改。正如您所看到的,该文件甚至不必是完全有效的 C++ 文件。它定义语法并扫描文件以查找与语法匹配的文本,从而生成 Python 变量。enum

#
# cpp_enum_parser.py
#
# Posted by Mark Tolonen on comp.lang.python in August, 2009,
# Used with permission.
#
# Parser that scans through C or C++ code for enum definitions, and
# generates corresponding Python constant definitions.
#
#

from pyparsing import *

# sample string with enums and other stuff
sample = """
    stuff before
    enum hello {
        Zero,
        One,
        Two,
        Three,
        Five=5,
        Six,
        Ten=10
        };
    in the middle
    enum blah
        {
        alpha,
        beta,
        gamma = 10 ,
        zeta = 50
        };
    at the end
    """

# syntax we don't want to see in the final parse tree
LBRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,")
_enum = Suppress("enum")
identifier = Word(alphas, alphanums + "_")
integer = Word(nums)
enumValue = Group(identifier("name") + Optional(EQ + integer("value")))
enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue))
enum = _enum + identifier("enum") + LBRACE + enumList("names") + RBRACE

# find instances of enums ignoring other syntax
for item, start, stop in enum.scanString(sample):
    id = 0
    for entry in item.names:
        if entry.value != "":
            id = int(entry.value)
        print("%s_%s = %d" % (item.enum.upper(), entry.name.upper(), id))
        id += 1
Run Code Online (Sandbox Code Playgroud)

输出:

HELLO_ZERO = 0
HELLO_ONE = 1
HELLO_TWO = 2
HELLO_THREE = 3
HELLO_FIVE = 5
HELLO_SIX = 6
HELLO_TEN = 10
BLAH_ALPHA = 0
BLAH_BETA = 1
BLAH_GAMMA = 10
BLAH_ZETA = 50
Run Code Online (Sandbox Code Playgroud)


Eth*_*man 5

改编 Mark Tolonen 的代码来创建实际的 Python 枚举:

# import the dependencies
from enum import EnumMeta, IntEnum
from pyparsing import Group, Optional, Suppress, Word, ZeroOrMore
from pyparsing import alphas, alphanums, nums
Run Code Online (Sandbox Code Playgroud)

第一步是创建一个新EnumMeta类型(请注意,这是子类化EnumMeta是一个好主意的时候)

CPPEnum = None
class CPPEnumType(EnumMeta):
    #
    @classmethod
    def __prepare__(metacls, clsname, bases, **kwds):
        # return a standard dictionary for the initial processing
        return {}
    #
    def __init__(clsname, *args , **kwds):
        super(CPPEnumType, clsname).__init__(*args)
    #
    def __new__(metacls, clsname, bases, clsdict, **kwds):
        if CPPEnum is None:
            # first time through, ignore the rest
            enum_dict = super(CPPEnumType, metacls).__prepare__(
                    clsname, bases, **kwds
                    )
            enum_dict.update(clsdict)
            return super(CPPEnumType, metacls).__new__(
                    metacls, clsname, bases, enum_dict, **kwds,
                    )
        members = []
        #
        # remove _file and _name using `pop()` as they will
        # cause problems in EnumMeta
        try:
            file = clsdict.pop('_file')
        except KeyError:
            raise TypeError('_file not specified')
        cpp_enum_name = clsdict.pop('_name', clsname.lower())
        with open(file) as fh:
            file_contents = fh.read()
        #
        # syntax we don't want to see in the final parse tree
        LBRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,")
        _enum = Suppress("enum")
        identifier = Word(alphas, alphanums + "_")
        integer = Word(nums)
        enumValue = Group(identifier("name") + Optional(EQ + integer("value")))
        enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue))
        enum = _enum + identifier("enum") + LBRACE + enumList("names") + RBRACE
        #
        # find the cpp_enum_name ignoring other syntax and other enums
        for item, start, stop in enum.scanString(file_contents):
            if item.enum != cpp_enum_name:
                continue
            id = 0
            for entry in item.names:
                if entry.value != "":
                    id = int(entry.value)
                members.append((entry.name.upper(), id))
                id += 1
        #
        # get the real EnumDict
        enum_dict = super(CPPEnumType, metacls).__prepare__(clsname, bases, **kwds)
        # transfer the original dict content, names starting with '_' first
        items = list(clsdict.items())
        items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p))
        for name, value in items:
            enum_dict[name] = value
        # add the members
        for name, value in members:
            enum_dict[name] = value
        return super(CPPEnumType, metacls).__new__(
                metacls, clsname, bases, enum_dict, **kwds,
                )
Run Code Online (Sandbox Code Playgroud)

创建新类型后,我们可以创建新的基类:

class CPPEnum(IntEnum, metaclass=CPPEnumType):
    pass
Run Code Online (Sandbox Code Playgroud)

一旦有了新的CPPEnum基类,使用它就非常简单:

class Hello(CPPEnum):
    _file = 'some_header.h'

class Blah(CPPEnum):
    _file = 'some_header.h'
    _name = 'blah'              # in case the name in the file is not the lower-cased
                                # version of the Enum class name (so not needed in
                                # in this case)
Run Code Online (Sandbox Code Playgroud)

并在使用中:

>>> list(Hello)
[
    <Hello.ZERO: 0>, <Hello.ONE: 1>, <Hello.TWO: 2>, <Hello.THREE: 3>,
    <Hello.FIVE: 5>, <Hello.SIX: 6>, <Hello.TEN: 10>,
    ]
Run Code Online (Sandbox Code Playgroud)

声明:我是Python stdlibEnumenum34backportAdvanced Enumeration ( aenum)库的作者 。