Reputation: 370
I am trying to call a C-function from Python that takes a function pointer as parameter. I need that function to be dynamically determined at runtime. Using ctypes
, that is pretty simple.
The C-code could look like this:
// my_c_funcs.h
double mul(double, double);
double add(double, double);
double do_something(double (*)(double, double), double, double);
// my_c_funcs.h
int mul(int a, int b) {
return a*b;
}
int add(int a, int b) {
return a + b;
}
int do_something(int (*f)(int, int), int a, int b) {
return f(a, b);
}
Having compiled that code into a shared library named "libMyCFuncs.so", I can pass a function that is determined at runtime in Python using ctypes
:
# foo.py
import ctypes
lib = ctypes.cdll.LoadLibrary("./libMyCfuncs.so")
def foo(func_name, a, b):
func = getattr(lib, func_name)
return lib.do_something(func, a, b)
I know I should define the return types but I left that out for brevity and used int
s only.
The code above gives the expected result, e.g. calling foo.foo('add', 2, 4)
yields 6
. However, I'd prefer using Cython because I make a lot of use of two- or higher-dimensional arrays and passing arrays is IMHO a lot easier in Cython. Let's say the Cython code is in "foo.pyx":
# foo.pyx
cdef extern from "my_c_funcs.h":
int mul(int, int)
int add(int, int)
int do_something(int (*)(int, int), int, int)
def foo(func_name, int a, int b):
# ???
Calling getattr
or even eval
obviously doesn't work. So how can I realize that in Cython?
Upvotes: 4
Views: 236
Reputation: 1703
You have to provide a wrapper from cython that can be called in your shared object. Cython essentially has three "modes"
As an example for your code a simple binding would be
cdef extern from "c_funcs.h":
double mul(double, double)
double add (double, double)
double do_something(double(*)(double, double), double, double)
cdef extern from "c_funcs.c":
pass
# this is callable from python
cpdef double py_do_something_mul(str name, double x, double y):
return do_something(mul, x, y)
If you want something like dynamic dispatching, you would have to provide some wrapping to this as well. It won't work with the default python dicts, but an ordered or unorered_map would work for this.
Upvotes: 2