gpt4 book ai didi

c++ - 带有指针 vector 的 SWIG 和 C++ 内存泄漏

转载 作者:IT老高 更新时间:2023-10-28 21:38:56 27 4
gpt4 key购买 nike

我正在使用 SWIG 在 C++ 和 Python 之间进行接口(interface)。我创建了一个创建对象指针的 std::vector 的函数。在这种情况下,指向的对象并不重要。

我遇到的问题是,当对象 (someObject) 超出 Python 端的范围时,它无法释放 vector 内对象/s 指针指向的内存,从而导致内存泄漏。

示例

  • 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在删除 vector 时,只能删除 vector 内的指针,而不能真正删除它们所指向的指针。

如果我可以为 std::vector 创建一个析构函数,这就是答案,但这是不可能的。

在有人建议将其作为解决方案之前,我确实需要使用与对象 vector 相对的指针 vector ,特别是因为对象又大又复杂,而且速度是一个问题。

我在 Windows 上使用 gcc4.4、swigwin 2.0.4 和 Python 2.7。

最佳答案

您看到的警告并不直接在于您有一个指针 vector 这一事实。考虑以下 SWIG 接口(interface)文件:

%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; }
%}

使用这个接口(interface)给出:

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++ 中的常用选项相同。我个人会尝试 avoid putting raw pointers in a vector除非您真的希望指向的对象比 vector 长。基本上你可以:

  1. 不在结构中存储指针
  2. 使用智能指针(std::shared_ptrstd::unique_ptr 或 boost 等价物代替)。
  3. 以某种方式手动管理内存。

我们已经在第二个示例中完成了 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
>>>

或者你可以 use %pythoncode 修改__del__自动调用该函数,但这是个坏主意,因为它不会影响 Python 根本看不到的对象,并且在某些情况下可能会导致意外行为。

关于c++ - 带有指针 vector 的 SWIG 和 C++ 内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13587791/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com