您看到的警告并不直接在于您有一个指针向量这一事实。考虑以下 SWIG 接口文件:
%module test
// This just gets passed straight through and not used for wrapping
%{
struct foo {};
%}
struct foo;
%inline %{
struct foo bar() { struct foo f; return f; }
%}
使用这个接口给出:
swig -Wall -python test.i && gcc -Wall -Wextra -std=c99 -shared -o _test.so test_wrap.c -I/usr/include/python2.7 && python2.7
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
>>> test.bar()
<Swig Object of type 'struct foo *' at 0xb7654a70>
>>>
swig/python detected a memory leak of type 'struct foo *', no destructor found.
问题是 SWIG 只看到了一个声明,而不是struct foo
. 默认行为是 Python 代理对象在此处释放/删除(视情况而定)底层对象,但它无法仅根据它所看到的前向声明来推断如何执行此操作。
如果我们扩展测试用例以包含std::vector<foo>
相同的内容,则会观察到:
%module test
%{
struct foo {};
%}
struct foo;
%include <std_vector.i>
%inline %{
foo bar() { return foo(); }
std::vector<foo> bar2() {
return std::vector<foo>();
}
%}
这再次给出了关于没有析构函数的警告:
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.bar2()
<Swig Object of type 'std::vector< foo,std::allocator< foo > > *' at 0xb7671a70>swig/python detected a memory leak of type 'std::vector< foo,std::allocator< foo > > *', no destructor found.
但是,我们可以通过确保类型的定义可用来轻松解决此问题。因为struct foo
这只是让 SWIG 可以看到整个结构体。因为std::vector<T>
我们需要使用%template
来做到这一点:
%module test
%include <std_vector.i>
%inline %{
struct foo {};
foo bar() { return foo(); }
std::vector<foo> bar2() {
return std::vector<foo>();
}
%}
%template(FooVec) std::vector<foo>;
现在没有警告(或泄漏):
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.bar()
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb76aba70> >
>>> print test.bar2()
<test.FooVec; proxy of <Swig Object of type 'std::vector< foo > *' at 0xb76abab8> >
>>>
复杂之处在于,在您的示例中,您有std::vector<T*>
,因此我们可以更改我们的测试用例来说明:
%module test
%include <std_vector.i>
%inline %{
struct foo {};
foo bar() { return foo(); }
std::vector<foo*> bar2() {
return std::vector<foo*>(1, new foo);
}
%}
%template(FooVec) std::vector<foo*>;
然后我们可以运行:
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.bar2()
<test.FooVec; proxy of <Swig Object of type 'std::vector< foo * > *' at 0xb7655a70> >
>>>
这确实会泄漏,但至关重要的是没有显示您注意到的警告,因为就 SWIG 而言,std::vector
它本身已被正确删除(实际上与 C++ 中的语义完全相同)。
至于如何处理泄漏,这些选项与 C++ 中的通常相同。就个人而言,我会尽量避免将原始指针放入向量中,除非你真的希望指向的对象比向量长。基本上你可以:
- 不在结构中存储指针
- 使用智能指针(
std::shared_ptr
或std::unique_ptr
或 boost 等价物)。
- 以某种方式手动管理内存。
我们已经在第二个示例中完成了 1。使用 SWIG 2 也非常简单,而 3 是在您的界面中编写和包装另一个函数的问题。
%module test
%include <std_vector.i>
%include <std_shared_ptr.i>
%{
#include <memory>
%}
%inline %{
struct foo {};
foo bar() { return foo(); }
std::vector<std::shared_ptr<foo> > bar2() {
return std::vector<std::shared_ptr<foo> >(1, std::make_shared<foo>());
}
%}
%shared_ptr(Foo);
%template(FooVec) std::vector<std::shared_ptr<foo> >;
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.bar2()
<test.FooVec; proxy of <Swig Object of type 'std::vector< std::shared_ptr< foo >,std::allocator< std::shared_ptr< foo > > > *' at 0xb76f4a70> >
>>> print test.bar2()[0]
<Swig Object of type 'std::vector< std::shared_ptr< foo > >::value_type *' at 0xb76f4a70>
>>>
哪个有效,存储共享指针并且不会泄漏。
如果你真的想做第三种方式(我会不惜一切代价避免它,因为它会让你的界面容易出现人为错误)使用 SWIG 实现它的最简单方法是使用%extend
,例如:
%module test
%include <std_vector.i>
%inline %{
struct foo {};
foo bar() { return foo(); }
std::vector<foo*> bar2() {
return std::vector<foo*>(1, new foo);
}
%}
%template(FooVec) std::vector<foo*>;
%extend std::vector<foo*> {
void empty_and_delete() {
for (std::vector<foo*>::iterator it = $self->begin();
it != $self->end(); ++it) {
delete *it;
}
$self->clear();
}
}
我们可以做的:
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
>>> x = test.bar2()
>>> print x.size()
1
>>> x.empty_and_delete()
>>> print x.size()
0
>>>
或者您可以使用%pythoncode
修改__del__
来自动调用该函数,但这不是一个好主意,因为它不会影响 Python 根本看不到的对象,并且在某些情况下可能会导致意外行为。