2

Using Cython (acting as an interface so I can call C++ based functions via Python), I am trying to wrap a complicated hierarchal structure of C++ classes, all of which eventually inherit from one base class. This singular base class has many virtual functions, whose implementation is different based on the derived class under consideration. Derived classes also have their own unique functions as well.

I am having a lot of trouble wrapping this via Cython, primarily because I can't find a way to re-define self.thisptr within each of the inherited classes to be of that specific type (rather than the type of the singular base class). I have also tried type-casting, as well as trying to delete the pointer and then re-initialize it, but it still thinks its pointing to the same base class. Example code is below:

inheritTest.pyx:

cdef extern from "BaseClass.h":
    cdef cppclass BaseClass:
        BaseClass() except +
        void SetName(string)
        float Evaluate(float)
        bool DataExists()

cdef extern from "DerivedClass.h":
    cdef cppclass DerivedClass(BaseClass):
        DerivedClass() except +
        void MyFunction()
        float Evaluate(float)
        bool DataExists()
        SetObject(BaseClass *)

cdef class PyBaseClass:
    cdef BaseClass *thisptr
    def __cinit__(self):
        self.thisptr = new BaseClass()
    def __dealloc__(self):
        del self.thisptr

cdef class PyDerivedClass(PyBaseClass):
    #This is one of the solutions that I have tried
    def __cinit__(self):
        if(self.thisptr):
            del self.thisptr
        self.thisptr = new DerivedClass()
    def __dealloc__(self):
        del self.thisptr
    def Evaluate(self, time):
        #I need to call the Derived implementation of this function, not Base
        return self.thisptr.Evaluate(self,time)
    def SetObject(self, PyBaseClass inputObject):
         #The issue here is that it looks for SetObject to be declared in BaseClass and spits an error otherwise. If I put a empty declaration, the complain is of ambiguous method overload
         self.thisptr.SetObject(inputObject.thisptr)

Also, when I call SetObject in my Python script, I want to be able to pass in a PyDerived Object as input, since it inherits from PyBase, shouldn't it just work? I tried it and the error is that:

Argument has incorrect type: expected PyBase, got PyDerived

Hopefully my questions make sense, please let me know if you require any additional information :)

1 Answer 1

1

Rather than deleting the old copies of self.thisptr, just make the constructor for the base class only initialize the pointer if the type of self is the base class.

def __cinit__(self):
    if type(self) is PyBaseClass:
        self.thisptr = new BaseClass()

Be sure you guard against deallocation the same way in the base class __dealloc__ method as well.

Then, to avoid a bunch of unnecessary casts, in the subclass wrapper, make two different attributes, one storing the pointer to the c++ object cast to the type of the base class and the other storing the pointer to the c++ object with its original type. Something like this:

cdef DerivedClass* derivedptr
def __cinit__(self):
    self.derivedptr = self.thisptr = new DerivedClass()

Then, when you need to access the methods of the derived class, use the derived pointer instead.

Be sure that, when deallocating, you only delete the derived pointer.

I got this idea from this discussion on the Cython Users mailing list.

Sign up to request clarification or add additional context in comments.

6 Comments

So I tried your above solution, it's giving me errors while building because it still thinks it's a BaseClass * pointer. The error is for example: **Object of type BaseClass has no attribute SetObject ** I have a temporary workaround with type-casting, but I would prefer if there is a cleaner solution
@jeet.m Updated to explain how to work around all the type casting.
Hmm..that's a good idea, so if I have a chain of derived classes, doesn't that mean I would need to redeclare a new type of "derivedptr" for each stage/level of derivation? (Ie. derivedptr1, derivedptr2 etc). If so, do you think this is a cleaner solution than type casting? Just want to know what you think is a better solution, not doubting your answer :)
Perfect, it works, thanks a lot! Do you have any ideas regarding the second part of my question, about being able to pass in a PyDerivedClass object as a function input argument?
It is definitely cleaner if you have multiple classes inheriting from the same base. As far as cleanliness goes, if you have lots of classes inheriting from one another, I'd just keep a base class pointer and then declare a property (named similarly to each class) for each class that does the casting for me. The docs describe how to declare properties. That'll avoid carrying around a bunch of different pointers, but it'll add a few lines of extra code in each class. It's just an idea though.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.