Ответ 1
Предупреждение, которое вы видите, прямо не связано с тем, что у вас есть вектор указателей. Рассмотрим следующий файл интерфейса 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
был удален (точно такая же семантика, как и на С++).
Что касается того, как справляться с утечкой, то в С++ параметры такие же, как обычно. Лично я бы попытался избегать размещения необработанных указателей в векторе, если вы действительно не хотите, чтобы объекты указывали на то, чтобы пережить вектор. В основном вы можете:
- Не хранить указатели в структуре
- Используйте интеллектуальные указатели (
std::shared_ptr
илиstd::unique_ptr
или более высокие эквиваленты). - Управляйте память вручную как-то.
Мы уже сделали 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 никогда не видит вообще и может привести к неожиданному поведению в нескольких случаях.