Can I expose a C++ enum
to SWIG as a real entity rather than a set of constants so I can enumerate over them in python code?
问题:
回答1:
I faced the same issue. I hope that SWIG soon supports C++11's enum class
.
Here's a hack that convinces SWIG to put enums in a structure:
#ifdef SWIG
%rename(MyEnum) MyEnumNS;
#endif
struct MyEnumNS
{
enum Value { Value1, Value2, Value3 };
};
typedef MyEnumNS::Value MyEnum;
In .cpp
code you now must use MyEnum::Value1
and in Python code it is MyEnum.Value1
. Although convoluted, the typedef
prevents having to change existing code that uses the enum everywhere and the SWIG %rename makes the enum have the same name in the SWIG wrapper.
In Python you can enumerate the values with a little code:
def values(enum):
return [(k,v) for k,v in vars(enum).items() if isinstance(v,int)]
It's not pretty, and I'd love to see a better solution.
回答2:
We can make something that lets you enumerate over it in Python, with relatively little intrusion into the C++ headers it wraps. For example if we have a header file:
#ifndef PYTHON_ENUM
#define PYTHON_ENUM(x) enum x
#endif
PYTHON_ENUM(TestName) {
foo=1,
bar=2
};
PYTHON_ENUM(SomeOtherName) {
woof,
moo
};
It expands to be just a regular enum in C++, but is sufficient as a header file to expose the enum members in Python.
Using %typemap(constcode)
we can inject some extra things into our Python module for the enum, but we need to know the name of the enum to do this; The SWIG typeinfo object for it is just as if it were an int
. Therefore we use a bit of a hack in our PYTHON_ENUM
macro to store the name of the enum in a custom typemap.
%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"
This creates a PyDict in the intermediate module for every enum that has key/value pairs. There's also some %pythoncode
glue there to tie the PyDict in the intermediate module into the exposed module. (I'm not sure how to refer to the intermediate module by name in it, other than hardcoded as _test - change as needed).
This is sufficient that I can then use it as:
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}
>>>
回答3:
I'm pretty sure you're looking for are...
typemaps: Since type handling is so central to wrapper code generation, SWIG allows it to be completely defined (or redefined) by the user. To do this, a special %typemap directive is used. (SWIG Doc2.0)
For all the info you could ever need about typemaps here is the link to the SWIG documentation about it. http://www.swig.org/Doc2.0/Typemaps.html#Typemaps_nn2
Typemaps should allow you to tell SWIG to convert c++ enums to python objects that you want.