0

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?

akerstjens
  • 23
  • 6

1 Answers1

1

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)
akerstjens
  • 23
  • 6