I've got a C++ class that has an attribute that is heap allocated and needs to be delete
d when the object is destructed. The result of writing a wrapper class and trying to instantiate it is a double-free.
test.h
#include <iostream>
int barIdCounter = 0;
class Foo
{
public:
Foo() {}
Foo(int x): x(x) {}
private:
int x;
};
class Bar
{
public:
Bar()
{
id = barIdCounter;
barIdCounter++;
std::cout << "constructing Bar " << id << std::endl;
foo = new Foo(1);
}
~Bar()
{
std::cout << "destructing Bar " << id << std::endl;
delete foo;
}
Foo* getFoo() const {return foo;}
int getId() const {return id;}
private:
Foo* foo;
int id;
};
module.pyx
# distutils: language = c++
# cython: language_level = 3
cdef extern from "test.h":
cdef cppclass Foo:
pass
cdef cppclass Bar:
Bar() except +
Foo* getFoo()
int getId()
cdef class PyBar:
cdef Bar cpp_bar
def __cinit__(self):
print("beginning PyBar.__cinit__")
self.cpp_bar = Bar()
print(f"self.cpp_bar has id {self.cpp_bar.getId()}")
print("exiting PyBar.__cinit__")
After compiling, running the command python3 -c "import module; b = module.PyBar()"
gives this output:
constructing Bar 0
constructing Bar 1
beginning PyBar.__cinit__
constructing Bar 2
destructing Bar 2
self.cpp_bar has id 2
exiting PyBar.__cinit__
destructing Bar 2
free(): double free detected in tcache 2
Aborted (core dumped)
I understand that the default constructor is called once when the PyBar
object is declared, because cpp_bar
is stack allocated, but why are there three Bar
s being constructed? And why is the third Bar
being destructed at all, forget about twice?