3

I've got a C++ class that has an attribute that is heap allocated and needs to be deleted 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 Bars being constructed? And why is the third Bar being destructed at all, forget about twice?

lol cubes
  • 125
  • 7
  • 2
    Your `Bar` is being copied (or moved), and it’s not safe to do that with its raw pointer handling. – Davis Herring Mar 04 '21 at 05:02
  • 1
    Have the destructor print its actual `this` pointer. This will prove it's not the *same* object being destructed multiple times. – dxiv Mar 04 '21 at 05:07
  • 2
    Related: https://stackoverflow.com/q/4172722/5769463, Because you don't define assignment operator you run into troubles. Yet not a duplicate, because the question how Cython handles allocation/deallocation of C++-objects is important here. – ead Mar 04 '21 at 06:22

0 Answers0