I have a C++ function ExecuteFunction
that takes as input another function f
.
I would like to expose ExecuteFunction
in Python with Boost.Python, and call it from Python with either Python functions or other C++ functions as arguments. I've found plenty of information on how to pass Python functions to ExecuteFunction
[1][2][3], but can't figure out how to specify a plain C++ function as argument.
Here is what I'm currently doing:
#include <boost/python.hpp>
typedef std::function<int(int)> Function;
int ExecuteFunction(int x, Function f) {
return f(x);
};
int Half(int x) {
return x / 2;
};
struct Square {
int operator()(int x) const {
return x * x;
};
};
BOOST_PYTHON_MODULE(Test) {
boost::python::def("ExecuteFunction", ExecuteFunction);
boost::python::def("Half", Half);
boost::python::class_<Square>("Square")
.def("__call__", &Square::operator());
};
On the Python side I would like to do the following:
import Test
def Double(x):
return x * 2
a = Test.ExecuteFunction(2, Test.Half)
b = Test.ExecuteFunction(2, Test.Square())
c = Test.ExecuteFunction(2, Double)
But the calls to Test.ExecuteFunction
fail with the following tracebacks respectively:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
Test.ExecuteFunction(int, Boost.Python.function)
did not match C++ signature:
ExecuteFunction(int, std::function<int (int)>)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
Test.ExecuteFunction(int, Square)
did not match C++ signature:
ExecuteFunction(int, std::function<int (int)>)
Boost.Python.ArgumentError: Python argument types in
Test.ExecuteFunction(int, function)
did not match C++ signature:
ExecuteFunction(int, std::function<int (int)>)
I'm not really interested in calling Half
and Square
on the Python side (although being able to do so would be nice). I would be satisfied with being able to specify the right argument to ExecuteFunction
. How could I do this?
As far as I can tell exposed C++ functions are always converted to a Python equivalent when constructed/invoked in Python. Hence ExecuteFunction
should take as input a boost::python::object
, not a std::function
, even if you only intend to pass C++ functions as argument. You can then call the function and retrieve its return value with boost::python::call
.
The following wound up working:
#include <boost/python.hpp>
int ExecuteFunctionWrapper(int x, const boost::python::object& f) {
return boost::python::call<int>(f.ptr(), x);
};
int Half(int x) {
return x / 2;
};
struct Square {
int operator()(int x) const {
return x * x;
};
};
BOOST_PYTHON_MODULE(Test) {
boost::python::def("ExecuteFunction", ExecuteFunctionWrapper);
boost::python::def("Half", Half);
boost::python::class_<Square>("Square")
.def("__call__", &Square::operator());
};
import Test
def Double(x):
return x * 2
a = Test.ExecuteFunction(4, Test.Half)
b = Test.ExecuteFunction(4, Test.Square())
c = Test.ExecuteFunction(4, Double)
print(a, b, c)