call cython classes from C++

2,048 views
Skip to first unread message

Sebastien Binet

unread,
Nov 19, 2010, 8:21:41 AM11/19/10
to cython-users

hi there,

I am unsure on how best to tackle it.
I have a C++ framework which makes use of inheritance and virtual
method. something like this:

// an instance implementing IAlgorithm is initialized once and then
// process data multiple times via 'execute' and gets a chance
// at producing some statistics once w/ 'finalize'
class IAlgorithm
{
public:
virtual int execute() = 0;
};

the application driver is then doing something like that:
StatusCode AppMgr::run()
{
typedef std::vector<IAlgorithm*>::iterator Itr_t;
for (Itr_t
itr = this->m_algs.begin(),
iend = this->m_algs.end();
itr != iend;
++itr) {
int sc = (*itr)->execute();
if (sc) {/*handle error*/}
}
}
// EOF

now, I'd like to provide users with the ability to implement their own
algorithms in cython.

AFAIK, it isn't possible (yet?) to inherit from a C++ class in cython
code neither it is to hope that proper redirections of method calls will
work.

so my plan was to have a little C++ wrapper class like that:

typedef int (*ExecFct_t)(void* obj);
class CyAlgBase : public IAlgorithm
{
public:
void* m_cy_obj;
ExecFct_t m_cy_fct;

void set_cy_state(void *cy_obj, ExecFct_t cy_fct)
{this->m_cy_obj = cy_obj; this->m_cy_fct = cy_fct;}

virtual int execute() { this->m_cy_fct(this->m_cy_obj); }
};

and have, on the cython side:
##
cdef extern from "CyAlgBase.h":
cdef cppclass CyAlgBase:
# ...

cdef class AlgBase:
cdef int execute(self):
print("AlgBase.execute!")

cdef int cy_fct_execute(void *obj):
cdef AlgBase alg = <AlgBase>(obj)
return alg.execute()

cdef public api IAlgorithm* create_alg(char* cls_name):
cdef CyAlgBase* alg = new CyAlgBase()
cdef AlgBase cy_alg = create_user_alg_from(cls_name)
alg.set_cy_state(<void*>cy_alg, <ExecFct_t>cy_fct_execute)
return <IAlgorithm*>alg
## EOF

then users would just have to inherit from AlgBase and implement their
own 'execute' method.

well, that's the plan (with the goal to by-pass most of the python C-API
overhead...)

anything doomed to fail ? (ref-counting ?)
anything which could be done better ?

cheers,
sebastien.

--
#########################################
# Dr. Sebastien Binet
# Laboratoire de l'Accelerateur Lineaire
# Universite Paris-Sud XI
# Batiment 200
# 91898 Orsay
#########################################

Robert Bradshaw

unread,
Nov 19, 2010, 2:54:21 PM11/19/10
to cython...@googlegroups.com

That is correct.

This method should work well, but you do need to manually do the
recounting. Specifically, do a PyIncref in set_cy_state (possibly with
a decref as well, if it's holding onto an old one), and of course
decref within the destructor. To simplify, it might make sense to take
the object pointer (I'd call it a PyObject* rather than a void*) in
the construtor, performing the incref there, and then put the decref
in the destructor, making it easy to get the refcounting correct by
tying it to the lifetime of the C++ instance.

When you get this working, I think it would be valuable example code
for others to put up on the wiki.

- Robert

Sebastien Binet

unread,
Nov 20, 2010, 6:41:04 AM11/20/10
to Robert Bradshaw, cython...@googlegroups.com
hi,

On Fri, 19 Nov 2010 11:54:21 -0800, Robert Bradshaw <robe...@math.washington.edu> wrote:
> > well, that's the plan (with the goal to by-pass most of the python C-API
> > overhead...)
> >
> > anything doomed to fail ? (ref-counting ?)
> > anything which could be done better ?
>
> This method should work well, but you do need to manually do the
> recounting. Specifically, do a PyIncref in set_cy_state (possibly with
> a decref as well, if it's holding onto an old one), and of course
> decref within the destructor. To simplify, it might make sense to take
> the object pointer (I'd call it a PyObject* rather than a void*) in
> the construtor, performing the incref there, and then put the decref
> in the destructor, making it easy to get the refcounting correct by
> tying it to the lifetime of the C++ instance.
>
> When you get this working, I think it would be valuable example code
> for others to put up on the wiki.

here it is:
http://bitbucket.org/binet/cy-cxxfwk/src

hth,
sebastien.

Chadrik

unread,
Dec 3, 2010, 3:57:02 AM12/3/10
to cython-users
hi,

this example was infinitely useful. thank you so much for posting it.
for my purposes, i need to expose a c++ class to python and allow
overriding of a virtual function there. i've altered your example to
get it working in both cython and python and made the handling of the
special cy_run_fct more automatic so that subclass instances in python
can be created in the standard way without the need for a helper
function.

-chad



Chadrik

unread,
Dec 3, 2010, 3:57:34 AM12/3/10
to cython-users

Dag Sverre Seljebotn

unread,
Dec 3, 2010, 4:10:49 AM12/3/10
to cython...@googlegroups.com
On 12/03/2010 09:57 AM, Chadrik wrote:
> hi,
>
> this example was infinitely useful. thank you so much for posting it.
> for my purposes, i need to expose a c++ class to python and allow
> overriding of a virtual function there. i've altered your example to
>

You mean that the C++ class has a virtual method, and that when
arbitrary C++ code calls that virtual method then Python code should be
executed?

I'm not sure if that is possible...AFAIK we don't ever generate C++
classes or even allow subclassing a C++ class in Cython.

Dag Sverre

Sebastien Binet

unread,
Dec 3, 2010, 4:14:32 AM12/3/10
to Chadrik, cython-users

glad that was useful.
please send me pull requests, all of this looks good to me :)

cheers,
sebastien.

>
> -chad

Sebastien Binet

unread,
Dec 3, 2010, 4:27:39 AM12/3/10
to Dag Sverre Seljebotn, cython...@googlegroups.com
On Fri, 03 Dec 2010 10:10:49 +0100, Dag Sverre Seljebotn <da...@student.matnat.uio.no> wrote:
> On 12/03/2010 09:57 AM, Chadrik wrote:
> > hi,
> >
> > this example was infinitely useful. thank you so much for posting it.
> > for my purposes, i need to expose a c++ class to python and allow
> > overriding of a virtual function there. i've altered your example to
> >
>
> You mean that the C++ class has a virtual method, and that when
> arbitrary C++ code calls that virtual method then Python code should be
> executed?
>
> I'm not sure if that is possible...AFAIK we don't ever generate C++
> classes or even allow subclassing a C++ class in Cython.
well, this example shows it is possible :)

this involves quite some scaffolding and it seems to me it could
theoretically be automated.

cheers,
sebastien.

Dag Sverre Seljebotn

unread,
Dec 3, 2010, 4:29:39 AM12/3/10
to cython...@googlegroups.com
On 12/03/2010 10:27 AM, Sebastien Binet wrote:
> On Fri, 03 Dec 2010 10:10:49 +0100, Dag Sverre Seljebotn<da...@student.matnat.uio.no> wrote:
>
>> On 12/03/2010 09:57 AM, Chadrik wrote:
>>
>>> hi,
>>>
>>> this example was infinitely useful. thank you so much for posting it.
>>> for my purposes, i need to expose a c++ class to python and allow
>>> overriding of a virtual function there. i've altered your example to
>>>
>>>
>> You mean that the C++ class has a virtual method, and that when
>> arbitrary C++ code calls that virtual method then Python code should be
>> executed?
>>
>> I'm not sure if that is possible...AFAIK we don't ever generate C++
>> classes or even allow subclassing a C++ class in Cython.
>>
> well, this example shows it is possible :)
>

OK, please pardon my laziness in this case.

Dag Sverre

Chadrik

unread,
Dec 3, 2010, 1:00:01 PM12/3/10
to cython-users

> this involves quite some scaffolding and it seems to me it could
> theoretically be automated.

yeah, a way to have this work natively would be fantastic.

Reply all
Reply to author
Forward
0 new messages