如何使用 SWIG 枚举枚举成员

Posted

技术标签:

【中文标题】如何使用 SWIG 枚举枚举成员【英文标题】:How to enumerate enum members using SWIG 【发布时间】:2012-11-07 12:02:33 【问题描述】:

我可以将 C++ enum 作为真实实体而不是一组常量公开给 SWIG,以便我可以在 python 代码中枚举它们吗?

【问题讨论】:

【参考方案1】:

我遇到了同样的问题。希望 SWIG 尽快支持 C++11 的enum class

这是说服 SWIG 将枚举放入结构中的技巧:

#ifdef SWIG
%rename(MyEnum) MyEnumNS;
#endif

struct MyEnumNS

    enum Value  Value1, Value2, Value3 ;
;
typedef MyEnumNS::Value MyEnum;

.cpp 代码中,您现在必须使用MyEnum::Value1,而在Python 代码中则为MyEnum.Value1。尽管令人费解,typedef 避免了必须更改在任何地方都使用枚举的现有代码,并且 SWIG %rename 使枚举在 SWIG 包装器中具有相同的名称。

在 Python 中,您可以用一些代码枚举值:

def values(enum):
    return [(k,v) for k,v in vars(enum).items() if isinstance(v,int)]

它并不漂亮,我希望看到更好的解决方案。

【讨论】:

是的,我在考虑这样的事情,但你的回答提供了一些现成的模式,谢谢。你能解释一下提到的sizeof问题吗?什么代码可能会完全中断,因为我不太明白。 我会删除那部分。这是我在尝试找到实现时正在考虑的事情,但我只是重新审视了它,这并不是一个真正的问题。【参考方案2】:

我们可以制作一些东西,让您在 Python 中对其进行枚举,而对它所包装的 C++ 标头的侵入相对较少。例如,如果我们有一个头文件:

#ifndef PYTHON_ENUM 
#define PYTHON_ENUM(x) enum x
#endif

PYTHON_ENUM(TestName) 
  foo=1,
  bar=2
;

PYTHON_ENUM(SomeOtherName) 
  woof,
  moo
;

它在 C++ 中扩展为只是一个常规枚举,但作为头文件足以在 Python 中公开枚举成员。

使用%typemap(constcode),我们可以为枚举注入一些额外的东西到我们的Python模块中,但是我们需要知道枚举的名称才能做到这一点;它的 SWIG 类型信息对象就好像它是一个 int。因此,我们在 PYTHON_ENUM 宏中使用了一些技巧来将枚举的名称存储在自定义类型映射中。

%module test
%
#include "test.h"
%

%typemap(constcode) int 
  PyObject *val = PyInt_FromLong(($type)($value));
  SWIG_Python_SetConstant(d, "$1", val);
  const char *name = "$typemap(enum_realname,$1_type)";
  PyObject *e = PyDict_GetItemString(d, name);
  if (!e) PyDict_SetItemString(d, name, e = PyDict_New());
  PyDict_SetItemString(e, "$value", val);

#define PYTHON_ENUM(x) \
        %typemap(enum_realname) int "x"; \
        %pythoncode % \
        x = _test.x\
        % \
        enum x

%include "test.h"

这会在中间模块中为每个具有键/值对的枚举创建一个 PyDict。还有一些%pythoncode 胶水将中间模块中的 PyDict 绑定到暴露的模块中。 (我不知道如何在其中通过名称来引用中间模块,除了硬编码为 _test - 根据需要更改)。

这就足够了,我可以将其用作:

Python 2.7.3 (default, Aug  1 2012, 05:16:07) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> print test.SomeOtherName
'woof': 0, 'moo': 1
>>> print test.TestName
'foo': 1, 'bar': 2
>>> 

【讨论】:

所以看起来这很困难是 SWIG 故意简单的结果,似乎他们没有考虑将 C/C++ 反射引入脚本语言,只是接口生成。看起来我不得不采用 Mark Tolonen 的解决方案,因为很少有人会理解你的解决方案,尽管我现在正处于理解它的边缘:D 谢谢你提供了一个使用类型图进行尝试的例子。 @unkulunkulu - 通常 SWIG 会努力让您在 C(或 C++)中做任何事情变得容易,但超出此范围往往更难。 @Flexo 不错的解决方案。我对其进行了扩展以导入字典,该字典支持键上的点补全,并将x = _test.x 替换为x = dotdict(_test.x),并在PYTHON_ENUM 宏之前添加了%pythoncode %from dicts import dotdict %【参考方案3】:

我很确定您正在寻找的是... typemaps:由于类型处理对于包装器代码生成非常重要,因此 SWIG 允许对其进行完全定义(或重新定义) ) 由用户。为此,使用了一个特殊的 %typemap 指令。 (SWIG Doc2.0)

对于您可能需要的关于类型映射的所有信息,这里是 SWIG 文档的链接。 http://www.swig.org/Doc2.0/Typemaps.html#Typemaps_nn2

Typemaps 应该允许你告诉 SWIG 将 c++ 枚举转换为你想要的 python 对象。

【讨论】:

@Flexo,tbh,我不知道类型映射,我是 SWIG 的新手,他们只是在项目的一些较暗部分使用它,它有效,但没人关心,但是然后我决定扩展一下。

以上是关于如何使用 SWIG 枚举枚举成员的主要内容,如果未能解决你的问题,请参考以下文章

为啥包含枚举的 C++ 方法会导致 SWIG/C# 中的 AccessViolationExceptions?

如何枚举 COM 对象成员,如接口、属性和方法?

如何定义枚举类型中枚举成员的映射?

SWIG 将 C++ 扩展到 Python,未定义的枚举

枚举类型的变量如何使用?

如何通过 MVC Razor 代码获取枚举成员的显示名称属性?