Inheritance: Passing Derived Class as Argument to Function expecting base class

499 views
Skip to first unread message

Jeet Mehta

unread,
Feb 19, 2015, 3:25:11 PM2/19/15
to cython...@googlegroups.com

I am using Cython to wrap a set of C++ classes, allowing a Python interface to them. Example Code is provided 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):
    def __cinit__(self):
        if(self.thisptr):
            del self.thisptr
        self.thisptr = new DerivedClass()
    def __dealloc__(self):
        del self.thisptr
    def Evaluate(self, time):
        return self.thisptr.Evaluate(self,time)
    def SetObject(self, PyBaseClass inputObject):
         # ***  The issue is right here ***
         self.thisptr.SetObject(<BaseClass *>inputObject.thisptr)

I want to be able to call SetObject in Python similar to as shown below:

main.py:

from inheritTest import PyBaseClass as base
from inheritTest import PyDerivedClass as der

a = der()
b = der()
a.SetObject(b)

I thought it would work since the classes inherit from each other, but its giving me the following error:

Argument has incorrect type: expected PyBaseClass, got PyDerivedClass

Not specifying type in the function definition makes it think that the inputObject is a pure Python object (has no C-based attributes, which it does), in which case the error is:

*Cannot convert Python object to BaseClass *

A sort-of hacky workaround to this just to have Python functions with different names that expect different types of arguments (ex: SetObjectWithBase, SetObjectWithDerived), and then within their implementation, just call the same C-based function having type-casted the input. I know for a fact this works, but I would like to avoid having to do this as much as possible. Even if there is a way I can catch the Type Error within the function, and deal with it inside, I think that might work, but I wasn't sure exactly how to implement that.

Hope this question makes sense, let me know if you require additional information.

Just wondering if this is a Cython bug, if not, how can I get around this

StackOverflow Link: http://stackoverflow.com/questions/28573479/cython-python-c-inheritance-passing-derived-class-as-argument-to-function-e/28615663#28615663

Nhai

unread,
Feb 19, 2015, 11:51:16 PM2/19/15
to cython-users
And

" self.thisptr.SetObject(<BaseClass *>inputObject.thisptr)"

should be

self.thisptr.SetObject(<BaseClass *>tmpobject.thisptr)

Hai

> On Feb 19, 2015, at 4:07 PM, Hai Nguyen <nha...@gmail.com> wrote:
>
> self.thisptr.SetObject(<BaseClass *>inputObject.thisptr)

Hai Nguyen

unread,
Feb 19, 2015, 11:51:18 PM2/19/15
to cython-users
I think you just remove `PyBaseClass` and cast python object inside the method. (check below)

def SetObject(self, inputObject):
        cdef PyBaseClass tmpobject = <PyBaseClass> inputObject

       
         # ***  The issue is right here ***
         self.thisptr.SetObject(<BaseClass *>inputObject.thisptr)

so now you can use either PyBaseClass or PyDerivedClass  objects in SetObject

Hai
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "cython-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to cython-users...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Robert Bradshaw

unread,
Feb 20, 2015, 12:54:25 AM2/20/15
to cython...@googlegroups.com
I can't reproduce this; are you sure your source is right?

Hai Nguyen

unread,
Feb 20, 2015, 1:41:24 PM2/20/15
to cython-users
Hi,

I might miss something (due to coffee drunk that I don't understand the problem (:D)) but this works for me.


Hai

Jeet Mehta

unread,
Feb 20, 2015, 2:10:31 PM2/20/15
to cython...@googlegroups.com
Hi everyone,

     Okay so I have experimented a bit, and narrowed the problem further, it turns out that it's not working for multiple levels of inheritance (works fine for single level). So for example:

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()
        void SetObject(BaseClass *)

cdef extern from "NextDerivedClass.h":
    cdef cppclass NextDerivedClass(DerivedClass):
        NextDerivedClass() except +
        #***Issue is here - Keeping the line below causes an ambigous overloaded method ***# 
        void SetObject(BaseClass *)

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

cdef class PyDerivedClass(PyBaseClass):
    cdef DerivedClass *derivedptr
    def __cinit__(self):
        self.derivedptr = self.thisptr = new DerivedClass()
    def __dealloc__(self):
        del self.derivedptr
    def SetObject(self, PyBaseClass inputObject):
         self.derivedptr.SetObject(<BaseClass *>inputObject.thisptr)

cdef class PyNextDerivedClass(PyDerivedClass):
    cdef NextDerivedClass *nextDerivedptr
    def __cinit__(self):
        self.nextDerivedptr = self.thisptr = new NextDerivedClass()
    def __dealloc__(self):
        del self.nextDerivedptr
    def SetObject(self, PyBaseClass input):
        self.nextDerivedptr.SetObject(<BaseClass *>input.thisptr)


In this case, keeping void SetObject(BaseClass *) in the declaration for the NextDerivedClass causes an "Ambigous Overloaded method" issue, not having it there allows me to call the function, but it obviously calls the inherited implementation (from DerivedClass). Can someone explain how to get around this issue, or if it is a bug?

Robert Bradshaw

unread,
Feb 25, 2015, 6:38:19 PM2/25/15
to cython...@googlegroups.com
You don't need to re-declare inherited methods. I don't know why you
think it'd be calling the DerivedClass implementation; that's not how
inheritance works. (I'm assuming here the method is virtual, if not
that's a poor API.)
Reply all
Reply to author
Forward
0 new messages