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?
In an unscoped enum, the scope is the surrounding scope; in a scoped enum, the scope is the enum-list itself. In a scoped enum, the list may be empty, which in effect defines a new integral type. class. By using this keyword in the declaration, you specify the enum is scoped, and an identifier must be provided.
In C++ programming, enum or enumeration is a data type consisting of named values like elements, members, etc., that represent integral constants. It provides a way to define and group integral constants. It also makes the code easy to maintain and less complex.
Two enum names can have same value. For example, in the following C program both 'Failed' and 'Freezed' have same value 0.
Enumerations make for clearer and more readable code, particularly when meaningful names are used. The benefits of using enumerations include: Reduces errors caused by transposing or mistyping numbers. Makes it easy to change values in the future.
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.
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}
>>>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With