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:
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:
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()
함수를 사용할 때, globals
와 locals
변수를 유지한채 두 번째 exec
호출시 import
한 내용이 반영되어 있지 않는 현상이 발견될 수 있다.
관련 현상은 Built-in Functions — Python 3.8.2 documentation 항목을 확인하면 된다.
__main__.__dict__
을 globals
로 사용해야 하고, globals
와 locals
변수는 동일한 객체를 참조해야 한다.
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());
}