How to pass python function as argument to C ++ function using Cython

Here are my settings: I have the following C ++ class that I want to wrap:

// Foo.h
class Foo
{
public:
  typedef int MyType;
  typedef int ArgType1;
  typedef int ArgType2;
  ...
  typedef MyType (*FooFunction) (ArgType1 a, ArgType2 b);
  ...
  void setFooFunction(FooFunction f);

      

An example of using this class in C ++:

#include "Foo.h"
...
int fooFun(int a, int b)
{
  if (a > b) return a;
  else return b;
}
...
int main(int argc, char **argv)
{
  ...
  fooObj->setFooFunction(&fooFun);
  ...
}  

      

Cython shell:

 # .pyx
 cdef extern from "Foo.h":
   cdef cppclass Foo:
     void setFooFunction(int *) except +

 def bar(fooFun):
   ...
   fooobj.setFooFunction(fooFun)
   ...

      

And I want to be able to do this:

 # python file
 ...
 def pyfun(x, y):
   return x + y
 ...
 def main():
   bar(pyfun)

      

I'm not completely familiar with Cython, but I've already tried to do the magic and it doesn't work:

 # .pyx
 cdef extern from "Foo.h":
   cdef cppclass Foo:
     void setFooFunction(int *) except +

 ctypedef int (*myFun) (int, int)

 def bar(fooFun):
   cdef myFun funpointer
   funpointer = (<myFun*><size_t>id(smoothfun))[0]
   ...
   fooobj.setFooFunction(<int*>fooFun)
   ...

      

Can you even do such a thing?

+3


source to share


1 answer


You can't easily: A C ++ function pointer just stores the location in memory where the code for that function starts (or something similar, concrete implementation), while a Python function is a full-fledged Python object with a dictionary storing byte- code, (possibly uncompiled Python code), docstrings and a few other bits. It's also not in a form that the machine can run on its own - it requires a Python interpreter to process the bytecode. There isn't really a way to store all of this in a C ++ function pointer.

What you can do is use C ++ 11 std::function

. This can be used as a function pointer and can take any callable object (something specific operator()

). The idea would be for your class to store std::function

instead of a function pointer.

#include <functional> // for std::function

class Foo {
  private:
    std::function<int(int,int)> stored_function;

  public:
    void setFooFunction(std::function<int(int,int)> f) {
      stored_function = f;
    }

    void doSomething() {
      // call it like this
      int result = stored_function(1,2);
    }
};

      

Then you pass in setFooFunction

a C ++ class that stores PyObject * (Python functions) and defines operator()

to call Python.

If you don't want to write a C ++ class yourself, the class boost::python::object

( http://www.boost.org/doc/libs/1_58_0/libs/python/doc/v2/object.html#object_operators-spec ) has the functionality you need, you can get PyObject * easily from Cython

from cpython.ref cimport PyObject
cdef PyObject* pyob_ptr = <PyObject*>some_function

      



and converting that to a boost C ++ class is reasonably simple too ( http://www.boost.org/doc/libs/1_58_0/libs/python/doc/tutorial/doc/html/python/object.html#python.creating_python_object )

boost::python::object o(boost::python::handle<>(boost::python::borrowed(pyobj_ptr)));

      

Since it o

is callable, you can use it directly instd::function

Foo foo; // a foo object
foo.setFooFunction(o); // pass it the boost::python::object

      

(Obviously a lot of detail is missing here, but hopefully a broad approach will be helpful!)

+1


source







All Articles