I'm trying to wrapp a C callback system. I want to call python methods (with python arguments) passing them as function callbacks to C function. A simple example<br><br>###### foo.h #######<br><br>define MAX_FUNCS 4<br>
<br>typedef void(*VoidFunc)(void*);<br> <br>typedef struct FooStruct{<br> VoidFunc funcs[MAX_FUNCS];<br> void* data[MAX_FUNCS];<br>} FooStruct;<br><br>FooStruct* Foo_new(void);<br><br>int Foo_call(FooStruct* self, int indexFunc);<br>
<br>int Foo_addFunc(FooStruct* self, VoidFunc func, void* data);<br><br>int Foo_fire(FooStruct* self);<br><br>void Foo_destroy(FooStruct* self);<br><br><br>###### foo.c #######<br>#include <stdlib.h><br>#include <foo.h><br>
<br>FooStruct* Foo_new(void){<br> int i;<br> FooStruct* tmp = malloc(sizeof(FooStruct));<br> for (i=0; i<MAX_FUNCS; i++){<br> tmp->funcs[i]=NULL;<br> tmp->data[i] = NULL;<br> }<br> return tmp;<br>}<br>
<br>int Foo_call(FooStruct* self, int indexFunc){<br> if (self)<br> if (indexFunc < MAX_FUNCS && indexFunc >= 0 ){<br> (*(self->funcs[indexFunc]))(self->data[indexFunc]);<br> return 0;<br>
}<br> return 1;<br>}<br><br>int Foo_addFunc(FooStruct* self, VoidFunc func, void* data){<br> if (self){<br> int i;<br> for (i=0; i<MAX_FUNCS; i++)<br> if (self->funcs[i] == NULL){<br> self->funcs[i] = func;<br>
self->data[i] = data;<br> break;<br> }<br> if (i != MAX_FUNCS)<br> return 0;<br> }<br> return 1;<br>}<br><br>int Foo_fire(FooStruct* self){<br> if (self){<br> int i;<br> for (i=0; i<MAX_FUNCS; i++)<br>
if (self->funcs[i])<br> (*(self->funcs[i]))(self->data[i]);<br> return 0;<br> }<br> return 1;<br>}<br><br>void Foo_destroy(FooStruct* self){<br> if (self)<br> free(self);<br>}<br><br><br>FooStruct structure stores a vector of void(*)(void*) function pointers and a vector of generic data (void pointers). The i function will be executed with the i data (void pointer) as argument. The Foo_fire method executes all function stored in a FooStruct instance with <span onclick="dr4sdgryt(event)">appropriate arguments. The bar.pyx wraps this functions in a pyrex extension PyFooStruct<br>
<br>cdef extern from 'foo.h':<br> struct FooStruct:<br> pass<br> FooStruct* Foo_new()<br> int Foo_call(FooStruct* self, int indexFunc)<br> int Foo_addFunc(FooStruct* self, void(*func)(void*), void* data)<br>
int Foo_fire(FooStruct* self)<br> void Foo_destroy(FooStruct* self)<br><br><br>cdef void PyCallback( object funcAndData):<br> cdef object func, data<br> func = funcAndData[0]<br> data = funcAndData[1]<br> func(*data)<br>
<br>cdef class PyFooStruct:<br> cdef FooStruct* selfPtr<br><br> def __init__(self):<br> self.selfPtr = Foo_new()<br><br> def PyFoo_call(self, i):<br> return Foo_call(self.selfPtr, i)<br><br> def PyFoo_addFunc(self, func, data):<br>
cdef object tmpFuncAndData<br> tmpFuncAndData = (func, data)<br> return Foo_addFunc(self.selfPtr, <void(*)(void*)>PyCallback, \<br> <void*>tmpFuncAndData)<br><br> def PyFoo_fire(self):<br>
return Foo_fire(self.selfPtr)<br><br> def __dealloc__(self):<br> Foo_destroy(self.selfPtr)<br><br><br>The PyCallback C function gets a tuple funcAndData as argument. The first element is a pointer to a Python function, the second is a tuple of generic data. PyCallback executes the function (first element of the tuple argument) with the data arguments (second item of the tuple argument). To add a python function reference to the PyFooStruct pyrex extension (using the Foo_addFunction C function), the PyFoo_addFunc pass the PyCallback method as the function pointer (casting its type to void(*)(void*)), and a tuple with two element as generic data (casting it to void*). This tuple (tmpFuncAndData), contains the python method reference as first argument (func) and the python func's argument as second argument (data). The steps through which a python function is executed by the C wrapped function are:<br>
<br>- PyFoo_addFunc store a callback to a func python function that use a tuple of data arguments, passing to Foo_addFunc PyCallback as the function pointer and a tuple (func, data) as argument (func is the python function, data the tuple of arguments);<br>
- when PyFoo_fire is called, all stored python function will be executed<br>- a generic i function is executed calling the PyCallback function with i void-casted data. <br>- PyCallback gets reference to python function and function data from the tuple argument, and call the function with the tuple argument.<br>
<br>I've written this simple python test:<br><br>from bar import *<br><br>def printFoo(*notUsedArgs):<br> print 'foo'<br><br>def printBar(*notUsedArgs):<br> print 'bar'<br><br>def printFooBar(*notUsedArgs):<br>
print 'foobar'<br><br>def printSomething( *tupleOfString ):<br> for foo in tupleOfString:<br> print foo<br><br>if __name__ == '__main__':<br> foo = PyFooStruct()<br> foo.PyFoo_addFunc(printFoo, (None,))<br>
foo.PyFoo_addFunc(printBar, (None,))<br> foo.PyFoo_addFunc(printFooBar, (None,))<br> foo.PyFoo_addFunc(printSomething, ('Foo', 'Bar', 'FooBar'))<br> foo.PyFoo_fire()<br> <br>But when I execute the script, the interpreter raises those exceptions<br>
<br>python test.py<br>Exception exceptions.TypeError: "'tuple' object is not callable" in 'bar.PyCallback' ignored<br>Exception exceptions.TypeError: "'tuple' object is not callable" in 'bar.PyCallback' ignored<br>
Exception exceptions.TypeError: "'tuple' object is not callable" in 'bar.PyCallback' ignored<br>Exception exceptions.TypeError: "'tuple' object is not callable" in 'bar.PyCallback' ignored<br>
python: Objects/obmalloc.c:929: PyObject_Free: Assertion `pool->ref.count > 0' failed.<br>Abortito<br><br>What is the problem? Could be a problem </span><span onclick="dr4sdgryt(event)">inherent </span><span onclick="dr4sdgryt(event)">to local scope of t</span><span onclick="dr4sdgryt(event)">mpFuncAndData variable in </span><span onclick="dr4sdgryt(event)">PyFoo_addFunc method? I've rewritten the wrapper importing the Py_XINCREF macro to increment the tmpFuncAndData reference count and all seems to work<br>
<br><br>######## bar.pyx with Py_XINCREF ######<br>cdef extern from 'Python.h':<br> struct PyObject:<br> pass<br> void Py_XINCREF(object)<br><br>cdef extern from 'foo.h':<br> struct FooStruct:<br> pass<br>
FooStruct* Foo_new()<br> int Foo_call(FooStruct* self, int indexFunc)<br> int Foo_addFunc(FooStruct* self, void(*func)(void*), void* data)<br> int Foo_fire(FooStruct* self)<br> void Foo_destroy(FooStruct* self)<br><br>
<br>cdef void PyCallback( object funcAndData):<br> cdef object func, data<br> func = funcAndData[0]<br> data = funcAndData[1]<br> func(*data)<br><br>cdef class PyFooStruct:<br> cdef FooStruct* selfPtr<br><br> def __init__(self):<br>
self.selfPtr = Foo_new()<br><br> def PyFoo_call(self, i):<br> return Foo_call(self.selfPtr, i)<br><br> def PyFoo_addFunc(self, func, data):<br> cdef object tmpFuncAndData<br> tmpFuncAndData = (func, data)<br>
Py_XINCREF(tmpFuncAndData)<br> return Foo_addFunc(self.selfPtr, <void(*)(void*)>PyCallback, \<br> <void*>tmpFuncAndData)<br><br> def PyFoo_fire(self):<br> return Foo_fire(self.selfPtr)<br>
<br> def __dealloc__(self):<br> Foo_destroy(self.selfPtr)<br><br>With the test script above, the interpreter gives me the correct output<br><br>python test.py<br>foo<br>bar<br>foobar<br>Foo<br>Bar<br>FooBar<br><br>Is there a way to solve the problem without importing anything from Python.h?<br>
</span>