SWIG、C++ 和 Python:过早删除 C++ 临时对象

Nic*_*ick 5 c++ python swig

我遇到了 SWIG 过早删除临时 C++ 对象的问题。

Python 测试脚本的输出示例:

--------------------------------------------------------------------------------
Works as expected:
    b0 =  Buffer(0, 0, 0, )
    b1 =  Buffer(1, 1, 1, )
    b0 =  Buffer(0, 0, 0, 1, 1, 1, )
    y  =  Buffer(0, 0, 0, 1, 1, 1, )
    b1 =  Buffer(1, 1, 1, )
    repr(b0) =  Buffer(id = 0, vector at 0x020bf450, data at 0x020aeb30, size = 6)
    repr(y)  =  Buffer(id = 0, vector at 0x020bf450, data at 0x020aeb30, size = 6)
Funny business:
    Deleting Buffer(id = 2)
    Deleting Buffer(id = 3)
    repr(b2) =  Buffer(id = 2, vector at 0x020bf790, data at 0x00, size = 4257068)
    Deleting Buffer(id = 4)
    repr(b3) =  Buffer(id = 4, vector at 0x02037040, data at 0x0204a4e0, size = 6)
    Deleting Buffer(id = 0)
    Deleting Buffer(id = 1)
Run Code Online (Sandbox Code Playgroud)

Deleting Buffer(id = X)正在从内部产生的Buffer::~Buffer()C ++代码,所以我们可以在这里看到,在Funny business一节中,C ++ Buffer对象越来越太早删除!Python的对象b2b3应持引用到C ++缓存使用对象id=2id=4

我所有的代码都附在我关于这个问题的博客文章中。但是,我将在这里总结代码:

缓冲区.hpp:

#include <vector>
#include <string>

struct Buffer
{
    Buffer();
    Buffer(const Buffer & copy);
    ~Buffer();

    Buffer & operator=(const Buffer & rhs);

    Buffer & operator<<(const Buffer & rhs);
    Buffer & operator<<(double rhs);

    std::string __str__() const;
    std::string __repr__() const;

    private:

    std::vector<double> _data;
    int                 _id;
};
Run Code Online (Sandbox Code Playgroud)

swig_test.i:

%module swig_test

%include "std_string.i"

%{
    #include "Buffer.hpp"
    #include <iostream>
%}

%ignore Buffer::operator=;

%include "Buffer.hpp"
Run Code Online (Sandbox Code Playgroud)

go_test.py:

from swig_test import Buffer


def zeros(n):
    '''
    Returns a Buffer filled with 'n' zeros.
    '''

    b = Buffer()

    for i in xrange(n):
        b << 0.0

    return b


def ones(n):
    '''
    Returns a Buffer filled with 'n' ones.
    '''

    b = Buffer()

    for i in xrange(n):
        b << 1.0

    return b


def main():

    #--------------------------------------------------------------------------
    # This sections works as expected

    print "-" * 80
    print "Works as expected:"

    b0 = zeros(3)

    print "    b0 = ", b0

    b1 = ones(3)

    print "    b1 = ", b1

    y = b0 << b1

    print "    b0 = ", b0
    print "    y  = ", y
    print "    b1 = ", b1

    print "    repr(b0) = ", repr(b0)
    print "    repr(y)  = ", repr(y)

    #--------------------------------------------------------------------------
    # Funny things are happening here!

    print "Funny business:"

    b2 = zeros(3) << ones(3)

    print "    repr(b2) = ", repr(b2)

    b3 = zeros(3) << 4.0

    print "    repr(b3) = ", repr(b3)


if __name__ == "__main__":
    main()
Run Code Online (Sandbox Code Playgroud)

我已经尝试了一些关于 SWIG 的事情,如我的博客文章中所述,但我的 SWIG-foo 技能变得很短。

拯救我 SO 社区,你是我唯一的希望!

更新 1

我怀疑我有多个PyObjects 持有Buffer *同一个 C++Buffer对象,所以当临时对象PyObject被垃圾收集时,它会Buffer *连同它一起删除 C++ 。

所以,我想我需要一个Py_INCREF地方,但在哪里?

更新 2

尝试按建议的值返回jarod42打破了串联范式,例如:

b = Buffer()
b << 1 << 2 << 3
print b
Run Code Online (Sandbox Code Playgroud)

只产生:

Buffer(1, )
Run Code Online (Sandbox Code Playgroud)

所以这不是我想要的。

%newobject指令可用于释放函数或方法创建的新创建的对象(防止内存泄漏)。在这种情况下,Buffer::operator<<不是创建新对象。

Nic*_*ick 3

经过更多搜索后,我发现了这个线程,最终找到了一个可行的解决方案。

typemap(out)结合使用 aPy_INCREF就达到了目的。

swig_test.i:

%module swig_test

%include "std_string.i"

%{
    #include "Buffer.hpp"
    #include <iostream>
%}

%ignore Buffer::operator=;

%typemap(out) Buffer & operator<<
{
    if(result) { /* suppress unused warning */ }
    Py_INCREF($self);
    $result = $self;
}

%include "Buffer.hpp"
Run Code Online (Sandbox Code Playgroud)

现在我得到了我想要的行为(与有效的纯 Python 实现相匹配)并且没有内存泄漏。