Function pointer as part of a C struct

172 views
Skip to first unread message

Alexander Eisenhuth

unread,
May 13, 2013, 5:50:51 AM5/13/13
to cython...@googlegroups.com
Hello everybody,

I want to use a callback as part of a C struct with read/write access.
Is this possible? The following describes the problem. And if you can
help, how do I connect a python function to the callback?

Any help welcome!

Regards
Alexander

---- stderr of compiling Device.pyx ---
Error compiling Cython file:
------------------------------------------------------------
...
def __setattr__(self, name, value):
print "DeviceDescriptor_.__setattr__", name, value
if name == "deviceNumber":
self._device_descriptor.deviceNumber = <Uint8> value
elif name == "deviceMeasurement":
self._device_descriptor.deviceMeasurement =
<DeviceMeasureFunc> value
^
------------------------------------------------------------
Device.pyx:55:56: Python objects cannot be cast to pointers of primitive
types

----- Device.h ----
typedef ErrorStatus (*DeviceMeasureFunc)(Uint32*);

typedef struct DeviceDescriptorTag
{
Uint8 deviceNumber;
DeviceMeasureFunc deviceMeasurement;

} DeviceDescriptor;
------- Device.pyx ---
cdef extern from "Device.h":

ctypedef ErrorStatus (*DeviceMeasureFunc)(Uint32*)

ctypedef struct DeviceDescriptor_c "DeviceDescriptor":
Uint8 deviceNumber
DeviceMeasureFunc deviceMeasurement

cdef class DeviceDescriptor:

cdef DeviceDescriptor_c _device_descriptor

def __getattr__(self, name):
ret_val = None
if name == "deviceNumber":
ret_val = self._device_descriptor.deviceNumber
else:
self.__raise_attrib_not_found(name)
return ret_val

def __setattr__(self, name, value):
if name == "deviceNumber":
self._device_descriptor.deviceNumber = <Uint8> value
elif name == "deviceMeasurement":
self._device_descriptor.deviceMeasurement =
<DeviceMeasureFunc> value
else:
self.__raise_attrib_not_found(name)

def __raise_attrib_not_found(self, name):
raise AttributeError("Attribute %s not in %s" % (name, type(self)))

Gregor Thalhammer

unread,
May 13, 2013, 7:55:23 AM5/13/13
to cython...@googlegroups.com
Am 13.5.2013 um 11:50 schrieb Alexander Eisenhuth:

Hello everybody,

I want to use a callback as part of a C struct with read/write access. Is this possible? The following describes the problem. And if you can help, how do I connect a python function to the callback?


It is not possible to directly use a Python callable as a C callback function. You need to define a custom callback (a cython cdef function), which further dispatches the call to a Python callable. You can find a demonstration of how to do this  at
Unfortunately, this described method requires that the C callback function gets another user defined argument that provides information about the Python function you want to call. Do you have influence on the signature of the  C callback? Otherwise you have to store the Python callback function in some global variable, which is a bad solution.

Gregor


Shriramana Sharma

unread,
May 13, 2013, 12:05:38 PM5/13/13
to cython...@googlegroups.com
On Mon, May 13, 2013 at 5:25 PM, Gregor Thalhammer
<gregor.t...@gmail.com> wrote:
> do this at
> cython/Demos/callback at master · cython/cython · GitHub
> Unfortunately, this described method requires that the C callback function
> gets another user defined argument that provides information about the
> Python function you want to call. Do you have influence on the signature of
> the C callback? Otherwise you have to store the Python callback function in
> some global variable, which is a bad solution.

I was working on this just a couple days back.

<rant>With all due respect to whoever wrote the example originally, I
found that it is not all that self-explanatory. Sometimes all these
semi-humorous examples are overdone and the technical aspect is lost
sight of. I mean, how am I supposed to immediately understand that the
void* user_data actually means the void* casted form of the Python
function object passed in? The functions and their arguments were not
at all given clear meaningful names.</rant>

I hence wrote up the attached examples, which I hope are more
meaningful to read and understand. The second one is a more condensed
form of the first, and the first is there only to indicate that if the
library you are wrapping doesn't already support (and most probably it
*won't* support) passing-the-ball with a void* py_callback, it is
trivial to write a C wrapper for that. (In the second, the same has
just been integrated into Cython so it's not really all that
different.)

Of course, in my example it would be possible to generate a list of
Fibonacci numbers in C and just pass that to Python to do whatever is
required without needing a feedback. But please remember this is
merely POC, and the same model is to be applied to the real situations
which *do* require a feedback.

The CTypes tutorial gives the libc qsort function as example. That
could also be implemented this way but I don't have the time to do
that.

--
Shriramana Sharma ஶ்ரீரமணஶர்மா श्रीरमणशर्मा
callback-examples-shriramana.tar.gz

Chris Barker - NOAA Federal

unread,
May 13, 2013, 1:26:39 PM5/13/13
to cython-users
On Mon, May 13, 2013 at 9:05 AM, Shriramana Sharma <sam...@gmail.com> wrote:
I hence wrote up the attached examples, which I hope are more
meaningful to read and understand.

It would be great if you could put this in the Cython Wiki:


(and put in a link in the relevant FAQ)

and/or make a pull request to add to the Demos in the source.

Now that you've done the work, it'd be nice to have it easy to find for newbies.

-Chris
 

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

Chris....@noaa.gov

Alexander Eisenhuth

unread,
May 15, 2013, 6:04:08 AM5/15/13
to cython...@googlegroups.com, Shriramana Sharma
Gregor,

thanks a lot for your example. It does hit 95 % my needs, but
unfortunately my callback, that I've to wrap has a pointer type as
argument, so I ran in:

Error compiling Cython file:
------------------------------------------------------------
...
cdef ErrorStatus deviceMeasurementWrapper(Uint32* value):
print "deviceMeasurementWrapper"
return (<object>cb_wrapper["deviceMeasurement"])(value)
^
------------------------------------------------------------
Device.pyx:77:59: Cannot convert 'Uint32 *' to Python object


Because I'm quite new to cython, it seems to me that it is a more
general question: How does cython handle pointer types? (for the python
side)

I've attached my files ... if I've missed something.

Regards
Alexander
device.zip

Gregor Thalhammer

unread,
May 15, 2013, 7:12:32 AM5/15/13
to cython...@googlegroups.com

Am 15.5.2013 um 12:04 schrieb Alexander Eisenhuth:

> Gregor,
>
> thanks a lot for your example. It does hit 95 % my needs, but unfortunately my callback, that I've to wrap has a pointer type as argument, so I ran in:
>
> Error compiling Cython file:
> ------------------------------------------------------------
> ...
> cdef ErrorStatus deviceMeasurementWrapper(Uint32* value):
> print "deviceMeasurementWrapper"
> return (<object>cb_wrapper["deviceMeasurement"])(value)
> ^
> ------------------------------------------------------------
> Device.pyx:77:59: Cannot convert 'Uint32 *' to Python object
>
>
> Because I'm quite new to cython, it seems to me that it is a more general question: How does cython handle pointer types? (for the python side)

I am not completely sure about this, I think you want to use '...(value[0])' to call the Python callback with the value of the 'value' pointer. This is the cython equivalent to '*value' in C.

Having a very quick look at your files it seems you are storing the Python callback in a global Python dict, so casting it again to a Python object is not necessary.

Gregor

Shriramana Sharma

unread,
May 18, 2013, 12:00:18 AM5/18/13
to cython...@googlegroups.com
On Wed, May 15, 2013 at 4:42 PM, Gregor Thalhammer
<gregor.t...@gmail.com> wrote:
>
>> Because I'm quite new to cython, it seems to me that it is a more general question: How does cython handle pointer types? (for the python side)
>
> I am not completely sure about this, I think you want to use '...(value[0])' to call the Python callback with the value of the 'value' pointer. This is the cython equivalent to '*value' in C.

I just read through Alexander's code and it would seem he is trying to
use a Python function to modify the data at a location pointed to by a
C pointer. DeviceMeasureFunc is a function type that returns an error
value and takes as argument a pointer to a uint32, presumably to
modify it. However IIUC, Python functions only take objects and return
objects -- they cannot modify specific locations (unless there is some
low-level Python library call that can do this?).

If he wants to pass the value generated by some device (temperature
reading of thermometer?) to Python then I presume what should be done
is to cdef a uint32 variable in Cython and pass its address to C along
with that of a cdef Cython callback (meant to be called from C), and
which then copies the value and passes it as a Python object up to
Python. (Am I making any sense?)

BTW your pyx should not have the same filename as the C/H files you
are wrapping, else the C file that Cython generates will overwrite
your C file which actually implements the functions. I append _cy to
the filename of the pyx for this reason.

Robert Bradshaw

unread,
May 19, 2013, 3:14:30 AM5/19/13
to cython...@googlegroups.com
On Fri, May 17, 2013 at 9:00 PM, Shriramana Sharma <sam...@gmail.com> wrote:
> On Wed, May 15, 2013 at 4:42 PM, Gregor Thalhammer
> <gregor.t...@gmail.com> wrote:
>>
>>> Because I'm quite new to cython, it seems to me that it is a more general question: How does cython handle pointer types? (for the python side)
>>
>> I am not completely sure about this, I think you want to use '...(value[0])' to call the Python callback with the value of the 'value' pointer. This is the cython equivalent to '*value' in C.
>
> I just read through Alexander's code and it would seem he is trying to
> use a Python function to modify the data at a location pointed to by a
> C pointer. DeviceMeasureFunc is a function type that returns an error
> value and takes as argument a pointer to a uint32, presumably to
> modify it. However IIUC, Python functions only take objects and return
> objects -- they cannot modify specific locations (unless there is some
> low-level Python library call that can do this?).
>
> If he wants to pass the value generated by some device (temperature
> reading of thermometer?) to Python then I presume what should be done
> is to cdef a uint32 variable in Cython and pass its address to C along
> with that of a cdef Cython callback (meant to be called from C), and
> which then copies the value and passes it as a Python object up to
> Python. (Am I making any sense?)

Yep.

> BTW your pyx should not have the same filename as the C/H files you
> are wrapping, else the C file that Cython generates will overwrite
> your C file which actually implements the functions. I append _cy to
> the filename of the pyx for this reason.
>
> --
> Shriramana Sharma ஶ்ரீரமணஶர்மா श्रीरमणशर्मा
>
> --
>
> ---
> 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/groups/opt_out.
>
>
Reply all
Reply to author
Forward
0 new messages