在 cython 中包装 typedefed 枚举

hfh*_*hc2 5 enums cython

我想在 cython 中包装以下代码:

enum Status {GOOD, BAD};

typedef enum Status STATUS;
// note that the typedef means people dont
// have to write `enum Status` everywhere

// just returns `GOOD`
STATUS hello();
Run Code Online (Sandbox Code Playgroud)

我在中编写了以下 cython 代码c_library.pxd

cdef extern from "library.h":

  cpdef enum Status:
    GOOD,
    BAD

  ctypedef Status STATUS

  cpdef STATUS hello()
Run Code Online (Sandbox Code Playgroud)

该模块c_library现在包含c_library.GOOD, c_library.BAD, and c_library.Status,其行为类似于enum。但是,函数调用的返回值hello返回一个普通的 int:

>>> c_library.hello()
0
>>> type(c_library.hello())
<class 'int'>
Run Code Online (Sandbox Code Playgroud)

我也希望将结果包装在相同类型的枚举中。我可以更改 cython 文件,但不能更改底层 C 代码。那可能吗?

ead*_*ead 3

这看起来像是 Cython 的一个小问题(小错误?),它决定在将-function__Pyx_PyInt_From_enum__包装到函数中时出于某些原因使用。cdefdef

作为一种快速解决方法,我可以建议显式创建一个Status-enum:

%%cython
cdef extern from *:
    """
    typedef enum Status {GOOD, BAD} Status;

    // just returns `GOOD`
    Status hello() {return GOOD;}
    """
    cpdef enum Status:
        GOOD,
        BAD

    Status c_hello "hello"()

def hello():
    return Status(c_hello())
Run Code Online (Sandbox Code Playgroud)

现在:

>>> type(hello())
<enum 'Status'>
Run Code Online (Sandbox Code Playgroud)

可能值得注意的事情:

  • 使用逐字 C 代码,使示例独立。
  • 使用typedef enum X {...} X;将枚举的类型名称从枚举的名称空间拉入普通变量的名称空间是一种常见的做法(但显然这是一个品味问题,所以如果您愿意,STATUS这取决于您)。请参阅 C11 标准中关于不同名称空间的措辞或这个很好的答案(即使它是关于struct)。
  • cname使用-trick (即Status c_hello "hello"()),能够添加间接级别并保持模块的公共接口完整(即cpdef hello())。
  • 但是,当使用helloas cdef-function 时,我可能会使用它c_hello来避免创建枚举的开销 - 这就是hello()定义为def-function 的原因,因此不会造成混淆。