call a Python function from c++ using pybind11

The answer to your question really has two parts: one about calling a Python function from C++, the other about embedding the interpreter.

Calling a function in pybind11 is simply a matter of getting that function into a pybind11::object variable, on which you can invoke operator() to attempt to call the object. (It doesn't have to be a function, but just something callable: for example, it could also be an object with a __call__ method). For example, to call math.sqrt(2) from C++ code you'd use:

auto math = py::module::import("math");
auto resultobj = math.attr("sqrt")(2);
double result = resultobj.cast<double>();

or you could condense it all to just:

double result = py::module::import("math").attr("sqrt")(2).cast<double>();

The second part of the question involves how to do this from a C++ executable. When building an executable (i.e. when your C++ code contains main()) you have to embed the Python interpreter in your binary before you can do anything with Python (like calling a Python function).

Embedded support is a new feature added in the current pybind11 master branch (which will become the 2.2 release). Here's a basic example that starts an embedded Python interpreter and calls a Python function (math.sqrt):

#include <pybind11/embed.h>
#include <iostream>

namespace py = pybind11;

int main() {
    py::scoped_interpreter python;

    auto math = py::module::import("math");
    double root_two = math.attr("sqrt")(2.0).cast<double>();

    std::cout << "The square root of 2 is: " << root_two << "\n";
}

Outputs:

The square root of 2 is: 1.41421

More examples and documentation of calling functions and embedding are available at http://pybind11.readthedocs.io/en/master/advanced/pycpp/object.html and http://pybind11.readthedocs.io/en/master/advanced/embedding.html, respectively.


Jasons answer is pretty much on point, but I want to add a slightly more complex (and clean) example calling a python method with a numpy input. I want to showcase two points:

  1. We can cast a py::object to a py::function using py::reinterpret_borrow<py::function>
  2. We can input a std::vector that automatically gets converted to a numpy.array

Note that the user is responsible for making sure that the PyModule.attr is actually a python function. Also note that the type conversion works for a wide variety of c++ types (see here for details).

In this example I want to use the method scipy.optimize.minimize with a starting point x0 that is provided from the c++ interface.

#include <iostream>
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>  // python interpreter
#include <pybind11/stl.h>  // type conversion

namespace py = pybind11;

int main() {
  std::cout << "Starting pybind" << std::endl;
  py::scoped_interpreter guard{}; // start interpreter, dies when out of scope

  py::function min_rosen =
      py::reinterpret_borrow<py::function>(   // cast from 'object' to 'function - use `borrow` (copy) or `steal` (move)
          py::module::import("py_src.exec_numpy").attr("min_rosen")  // import method "min_rosen" from python "module"
      );

  py::object result = min_rosen(std::vector<double>{1,2,3,4,5});  // automatic conversion from `std::vector` to `numpy.array`, imported in `pybind11/stl.h`
  bool success = result.attr("success").cast<bool>();
  int num_iters = result.attr("nit").cast<int>();
  double obj_value = result.attr("fun").cast<double>();
}

with the python script py_src/exec_numpy.py

import numpy as np
from scipy.optimize import minimize, rosen, rosen_der

def min_rosen(x0):
    res = minimize(rosen, x0)
    return res

Hope this helps someone!

Tags:

C++

Pybind11