如何为 Python 中的 class 类型应用 SWIG OUTPUT 类型映射?
How to apply SWIG OUTPUT typemaps for class types in Python?
我在使用 SWIG(版本 3.0.6)围绕 C++ 库生成 Python 包装器时遇到一些问题。
我的问题与应用 OUTPUT 类型映射有关,特别是在 pointers/references 到 class 类型的情况下。
为了说明,这就是我想要的标准类型,并且有效:
// .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",但它会自动附加到结果中。太棒了!
但是,当输出类型是指向 class 类型的指针时,这似乎并没有像我预期的那样工作:
// .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没有像simple类型那样处理。它仍然显示为包装函数签名中的 "input" 参数。
// 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 *&'
有人可以告诉我我做错了什么或指出正确的方向吗?任何帮助感激不尽!谢谢
这不是一个答案,只是没有足够的声誉来发表评论:(
因为您需要在 C++ 中使用指针,而 Python 没有指针(因此您无法对 Python 中当前的 'result' 执行任何操作)。
您能否按照@Jens Munk 的建议添加包装器以将指针隐藏到 .h 中:
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'。
我认为你需要使用指针。我也不确定在混合类型映射和 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 语句而不是类型映射更好。连接数组时,我建议使用 numpy.i 的预定义类型映射。
这个问题已经有一段时间没有解决了,所以我想我最好提供一个问题的解决方案。 OUTPUT 类型映射仅适用于简单类型,因此通过组合 in
和 argout
类型映射给出解决方案。
考虑一下我们有一个 C++ class SampleImpl
实现 C++ 接口 SampleBase
的情况,从技术上讲,它不是接口,因为它涉及虚拟析构函数的实现.假设我们有一个静态函数,其中 returns 一个错误代码和一个接口的实现。后者作为对指针的引用,也就是上面的情况。
接口header:
// 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;
};
}
实施header:
// 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) {
= &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(*),
$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
我在使用 SWIG(版本 3.0.6)围绕 C++ 库生成 Python 包装器时遇到一些问题。
我的问题与应用 OUTPUT 类型映射有关,特别是在 pointers/references 到 class 类型的情况下。
为了说明,这就是我想要的标准类型,并且有效:
// .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",但它会自动附加到结果中。太棒了!
但是,当输出类型是指向 class 类型的指针时,这似乎并没有像我预期的那样工作:
// .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没有像simple类型那样处理。它仍然显示为包装函数签名中的 "input" 参数。
// 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 *&'
有人可以告诉我我做错了什么或指出正确的方向吗?任何帮助感激不尽!谢谢
这不是一个答案,只是没有足够的声誉来发表评论:(
因为您需要在 C++ 中使用指针,而 Python 没有指针(因此您无法对 Python 中当前的 'result' 执行任何操作)。
您能否按照@Jens Munk 的建议添加包装器以将指针隐藏到 .h 中:
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'。
我认为你需要使用指针。我也不确定在混合类型映射和 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 语句而不是类型映射更好。连接数组时,我建议使用 numpy.i 的预定义类型映射。
这个问题已经有一段时间没有解决了,所以我想我最好提供一个问题的解决方案。 OUTPUT 类型映射仅适用于简单类型,因此通过组合 in
和 argout
类型映射给出解决方案。
考虑一下我们有一个 C++ class SampleImpl
实现 C++ 接口 SampleBase
的情况,从技术上讲,它不是接口,因为它涉及虚拟析构函数的实现.假设我们有一个静态函数,其中 returns 一个错误代码和一个接口的实现。后者作为对指针的引用,也就是上面的情况。
接口header:
// 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;
};
}
实施header:
// 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) {
= &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(*),
$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