Utilities
1. Using Python’s print function in C++
C++ 输出使用 std::cout, python 使用print。两种方法使用不同的buffers,混合可以导致 output order issues。为了解决这个问题,pybind11 module 提供了py::print 将输出流传递给python sys.stdout.
Python’s print function is replicated in the C++ API including optional keyword arguments sep, end, file, flush. Everything works as expected in Python:
py::print(1, 2.0, "three"); // 1 2.0 three
py::print(1, 2.0, "three", "sep"_a="-"); // 1-2.0-three
auto args = py::make_tuple("unpacked", true);
py::print("->", *args, "end"_a="<-"); // -> unpacked True <-
2. Capturing standard output from ostream
Often, a library will use the streams std::cout and std::cerr to print, but this does not play well with Python’s standard sys.stdout and sys.stderr redirection.
Replacing a library’s printing with py::print may not be feasible. This can be fixed using a guard around the library function that redirects output to the corresponding Python streams:
将C++module中的所有std::cout quan全部替换为python print恐怕不太现实,C++输出重定向至python可以通过 a guard around the library function 实现。
#include <pybind11/iostream.h>
...
// Add a scoped redirect for your noisy code
m.def("noisy_func", []() {
py::scoped_ostream_redirect stream(
std::cout, // std::ostream&
py::module::import("sys").attr("stdout") // Python output
);
call_noisy_func();
});
This method respects flushes on the output streams and will flush if needed when the scoped guard is destroyed.
这个允许输出重定向实时化,比如重定向至Jupyter Notebook。
两个可选参数(C++ stream和python output), 为默认的标准输出流,如果这两个参数没有给定。
An extra type, py::scoped_estream_redirect
, is identical except for defaulting to std::cerr
and sys.stderr
;
this can be useful with py::call_guard
, which allows multiple items, but uses the default constructor:
// Alternative: Call single function using call guard
m.def("noisy_func", &call_noisy_function,
py::call_guard<py::scoped_ostream_redirect,
py::scoped_estream_redirect>());
重定向也可以在python中完成,添加上下文管理(context manager),using the py::add_ostream_redirect()
function:
py::add_ostream_redirect(m, "ostream_redirect");
python中默认名为ostream_redirect
,会在python中创建如下的上下文管理器。默认会对两个流均进行重定向,也可以只对其中一个流进行重定向。
with ostream_redirect(stdout=True, stderr=True):
noisy_function()
Note
上述的方法不会对C/C++中的文件的输出流进行重定向,比如fprintf。
这种情境下需要直接在C中进行重定向,或者在python中重定向使用 os.dup2
function in an operating-system dependent way.
3. Evaluating Python expressions from strings and files
pybind11 提供eval
, exec
and eval_file
functions 来执行python语句表达式和文件,如下例:
// At beginning of file
#include <pybind11/eval.h>
...
// Evaluate in scope of main module
py::object scope = py::module::import("__main__").attr("__dict__");
// Evaluate an isolated expression
int result = py::eval("my_variable + 10", scope).cast<int>();
// Evaluate a sequence of statements
py::exec(
"print('Hello')\n"
"print('world!');",
scope);
// Evaluate the statements in an separate Python file on disk
py::eval_file("script.py", scope);
C++11 raw string literals are also supported and quite handy for this purpose. The only requirement is that the first statement must be on a new line following the raw string delimiter R"(
, ensuring all lines have common leading indent:
py::exec(R"(
x = get_answer()
if x == 42:
print('Hello World!')
else:
print('Bye!')
)", scope
);
Note
eval
and eval_file
有一个模板参数,该参数描述了这些string/file应该如何被interpreted。
可能的选项:
- eval_expr (isolated expression),
- eval_single_statement (a single statement, return value is always none)
- eval_statements (sequence of statements, return value is always none).
eval
defaults to eval_expr,
eval_file
defaults to eval_statements
exec
is just a shortcut for eval<eval_statements>.