Как применить SWIG OUTPUT typemaps для типов классов в Python?
У меня возникли некоторые проблемы с созданием оболочки Python вокруг библиотеки C++ с помощью SWIG (версия 3.0.6).
Моя проблема связана с применением выходной типовой карты, особенно в случае указателей / ссылок на типы классов.
Чтобы проиллюстрировать, это то, что я хочу для стандартных типов, и это работает:
// .h
int add(const long arg1,const long arg2,long& resultLong);
// interface.i
%apply long& OUTPUT { long& resultLong };
int add(const long arg1,const long arg2,long& resultLong);
// projectWrapper.py
def add(arg1, arg2):
return _projectWrapper.add(arg1, arg2)
addTerm = _projectWrapper.add
// usage
>>> result = projectWrapper.add(2, 4)
>>> print result
[0, 6L]
Вы не должны передавать "resultLong", но он добавляется к результату автоматически. Отлично!
Однако, это, кажется, не работает, как я ожидаю, когда тип вывода - это некоторый указатель на тип класса:
// .h
int GetClassType(const char* name, exportedClassType*& resultPointer);
class exportedClassType
{...}
// interface.i
%apply exportedClassType*& OUTPUT { exportedClassType*& resultPointer };
int GetClassType(const char* name, exportedClassType*& resultPointer);
// projectWrapper.py
def GetClassType(name, resultPointer):
return _projectWrapper.GetClassType(name, resultPointer)
GetClassType = _projectWrapper.GetClassType
Проблема, по-видимому, заключается в том, что SWIG не обработал его таким же образом, как простой тип. Он по-прежнему отображается как "входной" параметр в сигнатуре обернутой функции.
// attempted usage
>>> classType = projectWrapper.GetClassType("name")
TypeError: GetClassType() takes exactly 2 arguments (1 given)
>>> result = 0
>>> projectWrapper.GetClassType("name", result)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: in method 'GetClassType', argument 2 of type 'exportedClassType *&'
Может ли кто-нибудь, пожалуйста, сказать мне, что я делаю неправильно или указать мне правильное направление? Любую помощь с благодарностью принимаю! Спасибо3 ответа:
Этот вопрос долгое время оставался неразрешенным, поэтому я подумал, что лучше дать решение этому вопросу. Выходная типовая карта применяется только к простым типам, поэтому решение дается путем объединения
Рассмотрим ситуацию, когда мы имеем класс C++in
иargout
типовых карт.SampleImpl
, реализующий интерфейс C++SampleBase
, который технически не является интерфейсом, поскольку он включает в себя реализацию виртуального деструктора. Предположим, что у нас есть статическая функция, которая возвращает код ошибки и реализацию интерфейса. Последнее в качестве ссылки на указатель, что и является ситуацией выше.Заголовок интерфейса:
// Sample.hpp #pragma once namespace Module { class SampleBase { public: #ifndef SWIG // Hint to the programmer to implement this function static int SampleCreate(SampleBase *&obj); #endif virtual ~SampleBase() = default; }; }
Заголовок реализации:
// Sample_impl.hpp #pragma once #include "Sample.hpp" namespace Module { class SampleImpl : public SampleBase { public: static int SampleCreate(Module::SampleBase *&obj); SampleImpl(); virtual ~SampleImpl(); private: float a; }; }
Реализация:
// Sample_impl.cpp #include "Sample_impl.hpp" #include <cstdio> namespace Module { int SampleImpl::SampleCreate(Module::SampleBase*& obj) { obj = (SampleBase*) new SampleImpl(); return 0; } SampleImpl::SampleImpl() { printf("SampleImpl::SampleImpl()\n"); } SampleImpl::~SampleImpl() { printf("SampleImpl::~SampleImpl()\n"); } }
SWIG интерфейс (с использованием argout typemap)
// example.i %module example %{ #define SWIG_FILE_WITH_INIT #include "Sample.hpp" #include "Sample_impl.hpp" %} %include "typemaps.i" %typemap(in, numinputs=0) Module::SampleBase *&obj (Module::SampleBase *temp) { $1 = &temp; } %typemap(argout) Module::SampleBase *& { PyObject* temp = NULL; if (!PyList_Check($result)) { temp = $result; $result = PyList_New(1); PyList_SetItem($result, 0, temp); // Create shadow object (do not use SWIG_POINTER_NEW) temp = SWIG_NewPointerObj(SWIG_as_voidptr(*$1), $descriptor(Module::SampleBase*), SWIG_POINTER_OWN | 0); PyList_Append($result, temp); Py_DECREF(temp); } }
Использование в Python
import example // Creating specialization obj = example.SampleImpl() del obj // Creation of object using output typemap errorCode, obj = example.SampleImpl_SampleCreate() del obj
Это не ответ, просто недостаточно репутации для комментария : (
Потому что вам нужно использовать указатель в C++, а у Python нет указателей (так что вы все равно не смогли бы ничего сделать с вашим текущим "результатом" в Python).
Не могли бы вы добавить обертки, чтобы скрыть указатели .h как было предложено @Jens Munk:
class exportedClassType_ptr { public: exportedClassType* ptr; exportedClassType_ptr( exportedClassType& input ) { this->ptr = &input; } }; int GetClassType( const char* name, exportedClassType_ptr& resultPointer ) { return GetClassType( name, resultPointer.ptr ); }
Изменить .I файл для вызова нового метода:
%apply exportedClassType_ptr& OUTPUT { exportedClassType_ptr& resultPointer }; int GetClassType( const char* name, exportedClassType_ptr& resultPointer );
В Python напишите что-то вроде этого:
>>> realResult = projectWrapper.exportedClassType() >>> result = projectWrapper.exportedClassType_ptr(realResult) >>> projectWrapper.GetClassType("name", result)
И использовать "realResult" для будущей работы.
Я думаю, что вам нужно использовать указатели. Я также не уверен, что происходит, когда смешиваются typemaps и return-операторы. Минимальный пример файла
tst.i
:%module tst %{ // declaration: void add(long *resultLong, const long arg1,const long arg2); long mul(const long a, const long b); // the code: void add(long *resultLong, const long arg1,const long arg2) { *resultLong = arg1 + arg2; } long mul(const long a, const long b) { return a*b; } %} // The wrapper: %apply (long* OUTPUT) { long* resultLong }; void add(long* resultLong, const long arg1,const long arg2); long mul(const long a, const long b);
После перевода (я всегда использую CMake ), Использование в python будет:
import tst x = tst.add(3, 4) # results in 7L y = tst.mul(3, 4) # results in 12L
Я думаю, что лучше использовать операторы return вместо typemaps для скалярных типов данных. При взаимодействии массивов я рекомендую использовать предопределенные типовые карты numpy.i .