23

SWIGを使用してC++とPythonの間のインターフェイスを使用しています。オブジェクトポインタのstd::vectorを作成する関数を作成しました。この場合、ポイントされているオブジェクトは重要ではありません。

私が抱えている問題は、オブジェクト(someObject)がPython側のスコープから外れると、ベクトル内のオブジェクトのポインターが指すメモリを解放できず、メモリリークが発生することです。

  • C ++コード:

    std::vector < someObject* > createSomeObjectForPython()
    {
       std::vector < someObject* > myVector;
       someObject* instanceOfSomeObject = new someObject();
       myVector.push_back(instanceOfSomeObject);
       return myVector;
    }
    
  • Pythonインタープリターから:

    objectVar = createSomeObjectForPython()
    

これをPythonで実行すると、次のエラーが発生します。

swig/python detected a memory leak of type 'std::vector< someObject *,std::allocator<  someObject * > > *', no destructor found.

このエラーは、Pythonがベクターを削除するときに、ベクター内のポインターのみを削除でき、実際にはポインターが指しているものは削除できないためです。

std :: vectorのデストラクタを作成できれば、これが答えになりますが、それは不可能です。

特にオブジェクトが大きくて複雑であり、速度が問題であるため、誰かがこれを解決策として提案する前に、オブジェクトのベクトルではなくポインタのベクトルを使用する必要があります。

Windowsでgcc4.4、swigwin 2.0.4、Python2.7を使用しています。

4

1 に答える 1

45

表示される警告は、ポインターのベクトルがあるという事実に直接関係しているわけではありません。次の 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++ と同じです。個人的には、指しているオブジェクトをベクトルよりも長く存続させたい場合を除き、生のポインタをベクトルに配置することは避けたいと思います。基本的に、次のことができます。

  1. ポインタを構造体に格納しない
  2. スマート ポインターを使用します (std::shared_ptrまたはstd::unique_ptr代わりに同等のブーストを使用します)。
  3. 何らかの方法でメモリを手動で管理します。

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 がまったく認識しないオブジェクトに影響を与えず、いくつかのケースで予期しない動作につながる可能性があるため、それは悪い考えです。

于 2012-11-27T21:40:59.520 に答える