表示される警告は、ポインターのベクトルがあるという事実に直接関係しているわけではありません。次の 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 に関する限り、それ自体が正しく削除されているため (実際には C++ とまったく同じセマンティクス)、気づいた警告は表示されません。std::vector
リークに対処する方法に関しては、オプションは通常の C++ と同じです。個人的には、指しているオブジェクトをベクトルよりも長く存続させたい場合を除き、生のポインタをベクトルに配置することは避けたいと思います。基本的に、次のことができます。
- ポインタを構造体に格納しない
- スマート ポインターを使用します (
std::shared_ptr
またはstd::unique_ptr
代わりに同等のブーストを使用します)。
- 何らかの方法でメモリを手動で管理します。
2 番目の例では、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>
>>>
これは機能し、共有ポインターを保存し、リークしません。
本当に 3 番目の方法を実行したい場合 (インターフェースが人的ミスにさらされることを考えると、私はそれを絶対に避けたいと思います)、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 がまったく認識しないオブジェクトに影響を与えず、いくつかのケースで予期しない動作につながる可能性があるため、それは悪い考えです。