Sampsa Riikonen
2017-06-09 11:05:22 UTC
Dear List,
I am having hard time in interfacing python with C++ code featuring an
array of pointers.
My class constructor reserves an array of pointers with "new". Upon
destruction, that array of pointers is released with "delete[]". That
should be all legit.
There's no problem when running the code *without* python. However,
when I "swig'it" and use it as a python module, something weird
happens. The destructor is called correctly upon garbage collection,
but while doing that, a memory leak occurs. Please feel free to answer
alternatively/also here:
https://stackoverflow.com/questions/44455184/memory-leak-with-swig
(1) Preliminaries to get it compiled:
setup.py
from setuptools import setup, Extension, find_packages
import os
import copy
import sys
def make_pps():
d=[]
c=[]
l=[]
lib=[]
s=[]
s+=["-c++"]
return
Extension("_pps",sources=["pps.i","pps.cpp"],include_dirs=d,extra_compile_args=c,extra_link_args=l,libraries=lib,swig_opts=s)
ext_modules=[]
ext_modules.append(make_pps())
setup(
name = "pps",
version = "0.1",
packages = find_packages(),
install_requires = ['docutils>=0.3'],
ext_modules=ext_modules
)
pps.i
%module pps
%{
#define SWIG_FILE_WITH_INIT
%}
%inline %{
class MemoryManager {
public:
MemoryManager(int n);
~MemoryManager();
};
%}
(2) The C++ code itself:
pps.cpp
#include <iostream>
#include <stdio.h>
#include <typeinfo>
#include <sys/time.h>
#include <stdint.h>
#include <cmath>
#include <cstring>
#include <string.h>
#include <stdlib.h>
#include<sys/ipc.h> // shared memory
#include<sys/shm.h>
/*
Stand-alone cpp program:
- UNComment #define USE_MAIN (see below)
- compile with
g++ pps.cpp
- run with
./a.out
=> OK
- Check memory leaks with
valgrind ./a.out
=> ALL OK/CLEAN
Python module:
- Comment (i.e. use) the USE_MAIN switch (see below)
- compile with
python3 setup.py build_ext; cp
build/lib.linux-x86_64-3.5/_pps.cpython-35m-x86_64-linux-gnu.so ./_pps.so
- run with
python3 test.py
=> CRASHHHHH
- Check memory leaks with
valgrind python3 test.py
=> Whoa..
- Try to enable/disable lines marked with "BUG?" ..
*/
// #define USE_MAIN 1 // UNcomment this line to get stand-alone
c-program
using std::cout;
using std::endl;
using std::string;
class MemoryManager {
public:
MemoryManager(int n);
~MemoryManager();
private:
int nmax;
int nc; // next index to be used
uint nsum;
int* ids;
void** buffers;
};
MemoryManager::MemoryManager(int n) : nmax(n),nc(0) {
cout << "MemoryManager: nmax="<<this->nmax<<"\n";
this->buffers =new void*[this->nmax]; // BUG?
this->ids =new int [this->nmax];
this->nsum =0;
}
MemoryManager::~MemoryManager() {
printf("MemoryManager: destructor\n");
delete[] this->buffers; // BUG?
delete[] this->ids;
printf("MemoryManager: destructor: bye\n");
}
#ifdef USE_MAIN
int main(int argc, char *argv[]) {
MemoryManager* m;
m=new MemoryManager(1000);
delete m;
m=new MemoryManager(1000);
delete m;
}
#endif
(3) A test python program:
test.py
from pps import MemoryManager
import time
print("creating MemoryManager")
mem=MemoryManager(1000)
time.sleep(1)
print("clearing MemoryManager")
mem=None
print("creating MemoryManager (again)")
time.sleep(1)
mem=MemoryManager(1000)
time.sleep(1)
print("exit")
I am having hard time in interfacing python with C++ code featuring an
array of pointers.
My class constructor reserves an array of pointers with "new". Upon
destruction, that array of pointers is released with "delete[]". That
should be all legit.
There's no problem when running the code *without* python. However,
when I "swig'it" and use it as a python module, something weird
happens. The destructor is called correctly upon garbage collection,
but while doing that, a memory leak occurs. Please feel free to answer
alternatively/also here:
https://stackoverflow.com/questions/44455184/memory-leak-with-swig
(1) Preliminaries to get it compiled:
setup.py
from setuptools import setup, Extension, find_packages
import os
import copy
import sys
def make_pps():
d=[]
c=[]
l=[]
lib=[]
s=[]
s+=["-c++"]
return
Extension("_pps",sources=["pps.i","pps.cpp"],include_dirs=d,extra_compile_args=c,extra_link_args=l,libraries=lib,swig_opts=s)
ext_modules=[]
ext_modules.append(make_pps())
setup(
name = "pps",
version = "0.1",
packages = find_packages(),
install_requires = ['docutils>=0.3'],
ext_modules=ext_modules
)
pps.i
%module pps
%{
#define SWIG_FILE_WITH_INIT
%}
%inline %{
class MemoryManager {
public:
MemoryManager(int n);
~MemoryManager();
};
%}
(2) The C++ code itself:
pps.cpp
#include <iostream>
#include <stdio.h>
#include <typeinfo>
#include <sys/time.h>
#include <stdint.h>
#include <cmath>
#include <cstring>
#include <string.h>
#include <stdlib.h>
#include<sys/ipc.h> // shared memory
#include<sys/shm.h>
/*
Stand-alone cpp program:
- UNComment #define USE_MAIN (see below)
- compile with
g++ pps.cpp
- run with
./a.out
=> OK
- Check memory leaks with
valgrind ./a.out
=> ALL OK/CLEAN
Python module:
- Comment (i.e. use) the USE_MAIN switch (see below)
- compile with
python3 setup.py build_ext; cp
build/lib.linux-x86_64-3.5/_pps.cpython-35m-x86_64-linux-gnu.so ./_pps.so
- run with
python3 test.py
=> CRASHHHHH
- Check memory leaks with
valgrind python3 test.py
=> Whoa..
- Try to enable/disable lines marked with "BUG?" ..
*/
// #define USE_MAIN 1 // UNcomment this line to get stand-alone
c-program
using std::cout;
using std::endl;
using std::string;
class MemoryManager {
public:
MemoryManager(int n);
~MemoryManager();
private:
int nmax;
int nc; // next index to be used
uint nsum;
int* ids;
void** buffers;
};
MemoryManager::MemoryManager(int n) : nmax(n),nc(0) {
cout << "MemoryManager: nmax="<<this->nmax<<"\n";
this->buffers =new void*[this->nmax]; // BUG?
this->ids =new int [this->nmax];
this->nsum =0;
}
MemoryManager::~MemoryManager() {
printf("MemoryManager: destructor\n");
delete[] this->buffers; // BUG?
delete[] this->ids;
printf("MemoryManager: destructor: bye\n");
}
#ifdef USE_MAIN
int main(int argc, char *argv[]) {
MemoryManager* m;
m=new MemoryManager(1000);
delete m;
m=new MemoryManager(1000);
delete m;
}
#endif
(3) A test python program:
test.py
from pps import MemoryManager
import time
print("creating MemoryManager")
mem=MemoryManager(1000)
time.sleep(1)
print("clearing MemoryManager")
mem=None
print("creating MemoryManager (again)")
time.sleep(1)
mem=MemoryManager(1000)
time.sleep(1)
print("exit")