2

I have a cdef function that has amongst its parameters a function. I am trying to generate a python 'wrapper' function that will call it. I know that defining a function as cpdef() I would be able to get access to a python version of the function. However, if I do this, I will get an error (as expected) that says that python cannot recognize the function definition I provided.

Any suggestions?

My original function is domain specific and quite long but I think that the following example captures what I am after. I would have the following cdef() function defined,

ctypedef double (*ftype) (double)

cdef cy_myfunc(int a,..., double x, ftype f):
  ...
  cdef double result
  result = f(x)

  return result

and I would like to define something like the following so that I can call it in python:

def py_myfunc(a,..., x, f):
    return cy_myfunc(a,...,x,f)
marcos
  • 134
  • 15
  • 2
    All functions are objects as well. just pass a function if you need to pass one as if it was any other object. However, If your function is "wrapping" a function that is defined in it's own body, then you shouldn't be expecting any parameters for the "wrapper" function. It would be nice if you could write a code snippet to clarify what you are after. – Paritosh Singh Dec 04 '18 at 18:50
  • I amended my question above. – marcos Dec 04 '18 at 19:15
  • ok, so, i will admit i haven't worked with cython. But, first a question, why do you need to wrap this function in the first place? can you not just directly call cy_myfunc(a,...,x,f) ? – Paritosh Singh Dec 04 '18 at 19:27
  • 2
    If you want to convert a generic Python callable to a function pointer then see [the second half of this answer](https://stackoverflow.com/questions/34878942/using-function-pointers-to-methods-of-classes-without-the-gil/34900829#34900829). This is impossible in pure Cython since it needs to generate code at runtime – DavidW Dec 04 '18 at 22:21

1 Answers1

3

If you actually need to this (might think about refactoring so you don't) - you need some kind of PyObject to store the c function pointer.

The PyCapsule api provides a way of passing opaque pointers around in python space. Could do something like this, I'm probably missing some safety checks

%%cython
from cpython.pycapsule cimport PyCapsule_New, PyCapsule_GetPointer

ctypedef double (*ftype) (double)

# c function you want to wrapper
cdef double f(double a):
    return a + 22.0

# wrapper capsule
wrapped_f = PyCapsule_New(<void*>f, 'f', NULL)

cdef cy_myfunc(double x, ftype f):
  cdef double result = f(x)
  return result

def py_myfunc(double x, object f_capsule):
    cdef ftype f = <ftype> PyCapsule_GetPointer(f_capsule, 'f')
    return cy_myfunc(x, f)

Usage

wrapped_f
# Out[90]: <capsule object "f" at 0x0000000015ACFE70>

py_myfunc(2, wrapped_f)
# Out[91]: 24.0
chrisb
  • 49,833
  • 8
  • 70
  • 70
  • Thanks @chrisb. The code you have above works for an internal function cdef function. What if I wanted to pass a python function to py_myfunc() so that it is typecasted and then executed with the rest of the function. Essentially, I am trying to create a function that will accept different user functions provided that they accept a double and output a double. Apologies if this was not clear in my initial posting. I am still learning how to make sense of cython and its possibilities. – marcos Dec 05 '18 at 01:15
  • @marcos: The problem is that, for a Python function, there is no way to know whether it accepts a double and outputs a double. That kind of information isn't specified for Python functions. As has been asked here and on your other question, why don't you just let your Cython function accept an arbitrary object as `f`, and then try to call it? – BrenBarn Dec 05 '18 at 21:58