Reputation: 153
I have a base class LinkPredictor
and a sub class KatzIndex
which derives from LinkPredictor
in C++
.
Now I have another class which needs in its constructor a pointer to a LinkPredictor
.
I want to wrap those classes in Cython
to make them available in Python
.
CPP:
class LinkPredictor { ... }
class KatzIndex : public LinkPredictor { ... }
class KFoldCrossValidator {
public:
KFoldCrossValidator(LinkPredictor* lp) { ... }
}
Cython:
cdef extern from ".../LinkPredictor.h":
cdef cppclass _LinkPredictor:
_LinkPredictor(...) except +
cdef class LinkPredictor:
def __cinit__(self):
return
cdef extern from ".../KatzIndex.h":
cdef cppclass _KatzIndex(_LinkPredictor):
_KatzIndex(...) except +
...
cdef class KatzIndex(LinkPredictor):
cdef _KatzIndex* _this
...
cdef extern from ".../KFoldCrossValidator.h":
cdef cppclass _KFoldCrossValidator:
_KFoldCrossValidator(_LinkPredictor* linkPredictor) except +
...
cdef class KFoldCrossValidator:
cdef _KFoldCrossValidator* _this
def __cinit__(LinkPredictor linkPredictor):
self._this = new _KFoldCrossValidator(linkPredictor._this)
...
The approach above is not working. Cython throws the following error message:
Error compiling Cython file:
------------------------------------------------------------
...
cdef _KFoldCrossValidator* _this
def __cinit__(self, LinkPredictor linkPredictor):
self._this = new _KFoldCrossValidator(linkPredictor._this)
^
------------------------------------------------------------
.../project.pyx:X:Y: Cannot convert Python object to '_LinkPredictor *'
I think this happens because as _this
is only declared in KatzIndex
it has the type _KatzIndex*
instead of _LinkPredictor*
. Now I tried to declare the inheritance-relationship from _LinkPredictor
and _KatzIndex
(by declaring _KatzIndex(_LinkPredictor)
) and hoped that Cython would accept _this
with type _LinkPredictor*
as _KatzIndex
derives from _LinkPredictor
. But that doesn't seem to be the case.
What are your thoughts on this?
Upvotes: 3
Views: 374
Reputation: 7194
Your problem is that your python wrapper for the base class (LinkPredictor) will need to contain a pointer member which can then be overwritten by the derived classes.
As an example say we are wrapping the following c++ library:
foo.hpp
class Base {
public:
virtual double a();
};
class Derived : public Base {
public:
virtual double a();
};
class Using {
public:
double a;
Using(Base *b);
};
foo.cpp
#include "foo.hpp"
double Base::a()
{
return 1.0;
}
double Derived::a()
{
return 2.0;
}
Using::Using(Base *b) : a(b->a())
{}
We could then write the wrapper as
pyfoo.pyx
cdef extern from "foo.hpp":
cdef cppclass Base:
Base() except +
cdef cppclass Derived(Base):
Derived() except +
cdef cppclass Using:
Using(Base *b) except +
double a
cdef class PyBase(object):
cdef Base *_this
def __cinit__(self):
if type(self) != PyBase:
return
self._this = new Base()
def __dealloc__(self):
if self._this is not NULL:
del self._this
self._this = NULL
cdef class PyDerived(PyBase):
def __cinit__(self):
self._this = new Derived()
def __dealloc__(self):
if self._this is not NULL:
del self._this
self._this = NULL
cdef class PyUsing(object):
cdef Using *_this
def __cinit__(self, PyBase b):
self._this = new Using(b._this)
def a(self):
return self._this.a
def __dealloc__(self):
if self._this is not NULL:
del self._this
self._this = NULL
In addition to this you might want to read through this tutorial
Upvotes: 2