Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cython and C++ inheritance

I have 2 classes, A and B. B inherits from A.

//C++    
class A
{
    public:
        int getA() {return this->a;};
        A() {this->a = 42;}
    private:
        int a;

};

class B: public A
{
    public:
       B() {this->b = 111;};
       int getB() {return this->b;};
    private:
        int b;

};

Now I would like to interface those two classes using Cython and have the possibility to call the getA() method from a B instance:

a = PyA()
b = PyB()
assert a.getA() == b.getA()

Currently my pyx file looks like this:

cdef extern from "Inherit.h" :
    cdef cppclass A:
       int getA()

    cdef cppclass B(A):
       int getB()


cdef class PyA:
    cdef A* thisptr

    def __cinit__(self):
       print "in A: allocating thisptr"
       self.thisptr = new A()
    def __dealloc__(self):
       if self.thisptr:
           print "in A: deallocating thisptr"
           del self.thisptr

    def getA(self):
       return self.thisptr.getA()

cdef class PyB(PyA):
    def __cinit__(self):
       if self.thisptr:
          print "in B: deallocating old A"
          del self.thisptr
       print "in B: creating new b"
       self.thisptr = new B()

    def __dealloc__(self):
       if self.thisptr:
           print "in B: deallocating thisptr"
           del self.thisptr
           self.thisptr = <A*>0

    def getB(self):
       return (<B*>self.thisptr).getB()

While I hope this code is not doing anything too dangerous, I also hope there is a better way to handle it.

Also using the module generates the following output:

>>> from inherit import *
>>> b = PyB()
in A: allocating thisptr
in B: deallocating old A
in B: creating new b
>>> b.getA()
42
>>> b.getB()
111
>>> del b
in B: deallocating thisptr

And I don't really like allocating an A instance just to free it immediately after.

Any advice about how to do it correctly ?

like image 346
ascobol Avatar asked Apr 24 '12 13:04

ascobol


1 Answers

I make some experiments, and have quite ready answer but now i see where is the problem:

If your extension type has a base type, the __cinit__ method of the base type is automatically called before your __cinit__ method is called; you cannot explicitly call the inherited __cinit__ method.

So the real problem is that Cython types still do not have constructors, only pre initializer hook __cinit__ which behave more like default constructors. You cannot call virtual method from constructor and you cannot call it from __cinit__ either (if you make a call, it behave like non virtual).

Somehow inside __cinit__ the type(self) return correct type object, but it is useless. Cython do not have static fields, methods and type object can be only instance of type (no metaclasses). Python @staticmethod is easy overridable, so it is useless.

So there is no other way like to put allocation inside def __init__(self):, and check for initialized thisptr wherever you use it.

You may consider creating a global dummy C++ object, and assign it to thisptr to avoid checking and crashing. There are no post initializer hook, so you will be unable to check if correct initialization already have taken place.

like image 86
Arpegius Avatar answered Sep 21 '22 18:09

Arpegius