Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid memory leak with shared_ptr and SWIG

I'm trying to use boost::shared_ptr's to allow for me to use c++ file I/O stream objects in my python script. However, the generated wrapper warns me that it is leaking memory.

Here's a minimal .i file exhibiting the problem:

%module ptrtest

%include "boost_shared_ptr.i"
%include "std_string.i"

%shared_ptr( std::ofstream )

%{
#include <fstream>
#include <boost/shared_ptr.hpp>

typedef boost::shared_ptr< std::ofstream > ofstream_ptr;

ofstream_ptr mk_out(const std::string& fname ){
    return ofstream_ptr( new std::ofstream( fname.c_str() ) );
}

%}

ofstream_ptr mk_out(const std::string& fname );


%pythoncode %{

def leak_memory():
    ''' demonstration function -- when I call
        this, I get a warning about memory leaks
    ''''
    ostr=mk_out('/tmp/dont_do_this.txt')


%}

Here's the warning:

In [2]: ptrtest.leak_memory()
swig/python detected a memory leak of type 'ofstream_ptr *', no destructor found.

Is there a way to modify the .i file to tell the interface how to dispose of the shared_ptr properly?

like image 388
Dave Avatar asked Sep 18 '13 17:09

Dave


People also ask

Can you leak memory with shared pointers?

Cyclic reference problem with shared_ptr The problem with shared_ptr is that if there is a ring, or cycle of an objects that have shared_ptr to each other, they keep each other alive - they won't get deleted as they are holding each other (i.e, still has a shared_ptr pointing to them) leading to memory leak.


1 Answers

Your example is missing two parts to get the destructor to run:

  1. Since SWIG knows absolutely nothing about std::ofstream the default behaviour is to do nothing beyond pass an opaque handle around. See another answer of mine for a further discussion of this.

    The fix here is to supply an empty definition for std::ofstream in your interface file to convince SWIG it knows enough to do more, even if you don't plan on exposing any members.

  2. SWIG needs to see the typedef itself - inside the %{ %} it just gets passed straight to the output module, not used in the wraping itself.

Thus your example becomes:

%module ptrtest

%include "boost_shared_ptr.i"
%include "std_string.i"

%shared_ptr( std::ofstream )

namespace std {
  class ofstream {
  };
}

%{
#include <fstream>
#include <boost/shared_ptr.hpp>

typedef boost::shared_ptr< std::ofstream > ofstream_ptr;

ofstream_ptr mk_out(const std::string& fname ){
    return ofstream_ptr( new std::ofstream( fname.c_str() ) );
}
%}

typedef boost::shared_ptr< std::ofstream > ofstream_ptr;
ofstream_ptr mk_out(const std::string& fname );

%pythoncode %{
def leak_memory():
    ostr=mk_out('/tmp/dont_do_this.txt')
%}

For future reference you can avoid duplication of stuff that lives only in the .i file with %inline:

%inline %{
typedef boost::shared_ptr< std::ofstream > ofstream_ptr;

ofstream_ptr mk_out(const std::string& fname ){
    return ofstream_ptr( new std::ofstream( fname.c_str() ) );
}
%}

Which declares, defines and wraps it all in one shot.

like image 120
Flexo Avatar answered Sep 23 '22 00:09

Flexo