SWIG包装的向量的向量(C++到Python) - 如何识别内向量为代理对象?
我遇到了一个和包装 std::vector 的 std::vector,C++ SWIG Python类似的问题,但这不仅仅是简单的 C++ 解析。我在我的 C++ 代码中有以下内容:
namespace ns {
typedef unsigned long long uint64_t;
typedef std::vector<uint64_t> Vector;
typedef std::vector<Vector> VectorOfVectors;
class MyClass {
/// ...
/// Returns a reference to the internal vector allocated in C++ land
const VectorOfVectors &GetVectors() const;
};
}
在 SWIG 包装器中:
%module myswig
// ...
%template(Uint64V) std::vector<ns::uint64_t>;
%template(VUint64V) std::vector<std::vector<ns::uint64_t> >;
所以包装工作得很好,包括这个类,我可以正常获取这个类的向量的向量:
import myswig
m = myswig.MyClass()
v = m.GetVectors()
print v
这给了我:
<myswig.VUint64V; proxy of <Swig Object of type 'std::vector< std::vector< ns::uint64_t,std::allocator< ns::uint64_t > > > *' at 0x994a050> >
但是如果我访问向量中的一个元素,我得不到一个代理,它应该是 myswig.Uint64V - 这就是我的问题。
x = v[0]
print x
我希望得到的是:
<myswig.Uint64V; proxy of <Swig Object of type 'std::vector< ns::uint64_t, std::allocator< ns::uint64_t > > *' at 0x994a080> >
结果却是:
(<Swig Object of type 'ns::uint64_t *' at 0x994a080>, <Swig Object of type 'ns::uint64_t *' at 0x994a098>)
也就是说,访问向量的向量时,我得到的是一个包含两个元素的元组,而不是我需要的向量类的代理(这样访问内部向量就像访问其他向量一样简单)。
我还收到了这个警告:
swig/python detected a memory leak of type 'ns::uint64_t *', no destructor found.
因为当然这个类型没有定义析构函数。
有什么想法吗?
1 个回答
13
我和我的同事一起研究这个问题,最终找到了一些解决办法。
首先,在SWIG的.i文件中,定义一个预处理变量是非常重要的:
%{
# define SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS
%}
接下来,为了确保像front()、back()、operator[]等方法返回的引用能够正确映射到内部向量的代理类型,以下的类型映射是有帮助的:
// In pop()
%typemap(out) std::vector<std::vector<ns::uint64_t> >::value_type {
$result = SWIG_NewPointerObj(SWIG_as_voidptr(&$1), $descriptor(std::vector<ns::uint64_t>), 0 | 0 );
}
// In front(), back(), __getitem__()
%typemap(out) std::vector<std::vector<ns::uint64_t> >::value_type & {
$result = SWIG_NewPointerObj(SWIG_as_voidptr($1), $descriptor(std::vector<ns::uint64_t>), 0 | 0 );
}
我们还发现,如果你希望ns::uint64_t被当作Python中的长整型变量(相当于C语言中的无符号长整型),那么还需要一些额外的类型映射,以确保使用值和引用的向量方法能够直接使用64位整数值。
// In __getitem__()
%typemap(out) ns::uint64_t {
$result = PyLong_FromUnsignedLongLong($1);
}
// Not used (but probably useful to have, just in case)
%typemap(in) ns::uint64_t {
$1 = PyLong_AsUnsignedLongLong($input);
}
// In pop()
%typemap(out) std::vector<ns::uint64_t>::value_type {
$result = PyLong_FromUnsignedLongLong($1);
}
// In __getitem__(), front(), back()
%typemap(out) std::vector<ns::uint64_t>::value_type & {
$result = PyLong_FromUnsignedLongLong(*$1);
}
// In __setitem__(), append(), new Uint64Vector, push_back(), assign(), resize(), insert()
// This allows a python long literal number to be used as a parameter to the above methods.
// Note the use of a local variable declared at the SWIG wrapper function scope,
// by placing the variable declaration in parentheses () prior to the open brace {
%typemap(in) std::vector<ns::uint64_t>::value_type & (std::vector<ns::uint64_t>::value_type temp) {
temp = PyLong_AsUnsignedLongLong($input);
$1 = &temp;
}
希望这个解决方案能帮助到未来的人们!