Как перечислять перечисляемые члены с помощью SWIG

Можно ли выставить С++ enum в SWIG как реальную сущность, а не в набор констант, чтобы я мог перечислить их в коде python?

Ответ 1

Я столкнулся с той же проблемой. Я надеюсь, что SWIG скоро поддержит С++ 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% делает имя перечисления с тем же именем в оболочке SWIG.

В Python вы можете перечислить значения с помощью небольшого кода:

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

Это некрасиво, и мне бы хотелось увидеть лучшее решение.

Ответ 2

Мы можем сделать что-то, что позволяет перечислить его в Python, с относительно небольшим вторжением в заголовки С++, которые он обертывает. Например, если у нас есть файл заголовка:

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

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

PYTHON_ENUM(SomeOtherName) {
  woof,
  moo
};

Он расширяется, чтобы быть просто регулярным перечислением в С++, но является достаточным как заголовочный файл для отображения элементов перечисления в Python.

Используя %typemap(constcode), мы можем добавить некоторые дополнительные вещи в наш модуль Python для перечисления, но для этого нам нужно знать имя enum; Объект типа 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}
>>> 

Ответ 3

Я уверен, что вы ищете...
typemaps. Поскольку обработка типов настолько важна для генерации кода оболочки, SWIG позволяет полностью определять (или переопределять) пользователя. Для этого используется специальная директива% typemap. (SWIG Doc2.0)

Для всей информации, которая вам когда-либо понадобилась о typemaps, приведена ссылка на документацию SWIG об этом. http://www.swig.org/Doc2.0/Typemaps.html#Typemaps_nn2

Typemaps должен позволить вам сообщать SWIG, чтобы конвертировать С++ перечисления в объекты python, которые вы хотите.