Skip to content

Pybind11

Seamless operability between C++11 and Python.

Export function example

아래의 코드를 컴파일 하여, example.so파일을 생성한다. 중요한 점은 반드시 Python 라이브러리를 링크해야 한다.

#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

    m.def("add", &add, "A function which adds two numbers");
}

Python code:

>>> import example
>>> example.add(1, 2)
3L
>>>

Export variable example

PYBIND11_MODULE(example, m) {
    m.attr("the_answer") = 42;
    py::object world = py::cast("World");
    m.attr("what") = world;
}

These are then accessible from Python:

>>> import example
>>> example.the_answer
42
>>> example.what
'World'

Creating bindings for a custom type

Let’s now look at a more complex example where we’ll create bindings for a custom C++ data structure named Pet. Its definition is given below:

struct Pet {
    Pet(const std::string &name) : name(name) { }
    void setName(const std::string &name_) { name = name_; }
    const std::string &getName() const { return name; }

    std::string name;
};

The binding code for Pet looks as follows:

#include <pybind11/pybind11.h>

namespace py = pybind11;

PYBIND11_MODULE(example, m) {
    py::class_<Pet>(m, "Pet")
        .def(py::init<const std::string &>())
        .def("setName", &Pet::setName)
        .def("getName", &Pet::getName);
}

Python code:

>>> import example
>>> p = example.Pet('Molly')
>>> print(p)
<example.Pet object at 0x10cd98060>
>>> p.getName()
u'Molly'
>>> p.setName('Charly')
>>> p.getName()
u'Charly'

Exposing buffer

// myextension.cpp

#include <pybind11/pybind11.h>
#include <opencv2/opencv.hpp>

// Some function that returns an OpenCV image object (cv::Mat)
cv::Mat get_image();

PYBIND11_MODULE(myextension, m) {

    m.def("get_image", get_image);

    pybind11::class_<cv::Mat>(m, "Image", pybind11::buffer_protocol())
        .def_buffer([](cv::Mat& im) -> pybind11::buffer_info {
            return pybind11::buffer_info(
                // Pointer to buffer
                im.data,
                // Size of one scalar
                sizeof(unsigned char),
                // Python struct-style format descriptor
                pybind11::format_descriptor<unsigned char>::format(),
                // Number of dimensions
                3,
                // Buffer dimensions
                { im.rows, im.cols, im.channels() },
                // Strides (in bytes) for each index
                {
                    sizeof(unsigned char) * im.channels() * im.cols,
                    sizeof(unsigned char) * im.channels(),
                    sizeof(unsigned char)
                }
            );
        });
}

Python code:

import numpy as np
import myextension

def get_image():
    im = myextension.get_image()
    return np.array(im, copy=False)

exec() 에서 전역 변수 호출 문제

pybind11::exec() 함수를 사용할 때, globalslocals 변수를 유지한채 두 번째 exec호출시 import한 내용이 반영되어 있지 않는 현상이 발견될 수 있다.

관련 현상은 Built-in Functions — Python 3.8.2 documentation 항목을 확인하면 된다.

__main__.__dict__globals로 사용해야 하고, globalslocals 변수는 동일한 객체를 참조해야 한다.

namespace py = pybind11;
auto const * const TEST_PYTHON_CODE = " ... ";
auto globals = py::globals();
py::exec(TEST_PYTHON_CODE, globals, globals);

참고로 globals()함수는 아래와 같은 구현이다.

/// \ingroup python_builtins
/// Return a dictionary representing the global variables in the current execution frame,
/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded).
inline dict globals() {
    PyObject *p = PyEval_GetGlobals();
    return reinterpret_borrow<dict>(p ? p : module::import("__main__").attr("__dict__").ptr());
}

Troubleshooting

Mix Module & Interpreter

See also

Favorite stie