Boost.Python is often used when creating a library for Python using C ++, but it can also be used for so-called "embedding" that uses Python code from the C ++ side.
Isn't there much demand for using Python code from C ++? Even if you search for "boost python" etc., you will only get articles about the opposite use (when using C ++ code from Python). In that case, including the search keyword "embed" makes it easier to hit articles when embedding Python in C ++.
main.cpp
#include <boost/python.hpp>
namespace py = boost::python;
int main()
{
Py_Initialize(); //Must be called first
try
{
//In Python "print('Hello World!')Execute
py::object global = py::import("__main__").attr("__dict__");
py::exec("print('Hello World!')", global);
}
catch (const py::error_already_set &)
{
//If an error occurs while executing Python code, the error content is displayed.
PyErr_Print();
}
return 0;
}
In this sample, the py :: exec ()
function is used to simply execute the Python code print ('Hello World!')
.
I'm running my code in the global namespace on Python by getting an object in the global namespace on Python in a variable called global
.
Boost.Python calls Python's C interface behind the scenes, so you need to call the Py_Initialize ()
function before using it.
(* According to the official tutorial, Py_Finalize ()
should not be called in the code)
Also, if a runtime error occurs in Python, an exception of type py :: error_already_set
will be thrown, so catch this and display the contents withPyErr_Print ()
.
For Linux + GCC, you can compile with the following command.
How to compile
$ g++ main.cpp `pkg-config python3-embed --cflags --libs` -lboost_python38
Depending on the environment, there may be a version without python3-embed
, so change it to pkg-config python3
as appropriate (in the version with python3-embed
, the library for embedding is excluded from the original So using pkg-config python3
may result in a link error).
Also, the boost_python38
part of the above depends on the version and environment (it may be in the form of boost_python-py38
).
If you don't know the name of the Boost.Python library to link to, you can look it up with the following command:
The **
part of lib **. So
that appears is the library name.
Boost.How to find out if you don't know the Python library name
$ ldconfig -p | grep "boost_python3"
From here, we will see how to use each specific function.
Hereafter, assuming that namespace py = boost :: python;
is written, it is written as py
.
Also, global
in the code is an object in the global namespace obtained in the code below, as in the first sample.
py::object global = py::import("__main__").attr("__dict__");
py :: eval ()
: Evaluate an expression in Python and return the resultIf you give a Python expression as a string, it will return the result as py :: object
type.
You can get the result as a C ++ type by combining it with py :: extract <type name>
as shown below.
// "2**3"Get the result of
py::object result = py::eval("2**3", global);
//Convert the result to int type and output
std::cout << py::extract<int>(result) << std::endl;
Execution result
8
In the case of this example, the function in Python is not used in particular, so it works even if the namespace global
of the second argument is omitted.
However, please note that if you omit the second argument, you will not be able to use Python built-in functions such as pow
or variables / functions that you have defined separately.
py :: exec ()
: Pass Python code as a string and executeAs was the case with the first sample, if you give the py :: exec ()
function the Python code as a string, it will be executed as it is.
Unlike py :: eval ()
, it can also execute multiple commands and define classes, separated by newlines.
py::exec(
"print('Hello!')\n"
"print('World!')", global);
Execution result
Hello!
World!
When a new variable is created, it will be generated on the namespace given in the second argument.
//2 in the result variable in Python's global namespace**Substitute 3
py::exec("result = 2**3", global);
//Convert the result to int type and output
std::cout << py::extract<int>(global["result"]) << std::endl;
Execution result
8
py :: exec_file ()
: Execute Python code from a fileYou can use py :: exec_file ()
to read the contents of a file and execute it in the same way as py :: exec ()
.
The first argument is the file path to the Python script.
py::exec_file("hello.py", global);
This is useful when you want to prepare the definition of your own class as a Python script file separately from C ++.
py :: object :: attr ()
: Object field / method referenceFields (member variables) and methods (member functions) of py :: object
can be used by using the ʻattr ()` function.
Example of calling str type join method
// ['hoge','fuga','piyo']Create a list called
py::object list = py::eval("['hoge','fuga','piyo']", global);
// 「','.join(list), And combine the strings in the list separated by commas.
py::object result = py::object(",").attr("join")(list);
//Convert the result to string type and output
std::string resultStr = py::extract<std::string>(result);
std::cout << resultStr << std::endl;
Execution result
hoge,fuga,piyo
py :: import ()
: Using the Python libraryYou can use py :: import
to import a library in the same way as Python's ʻimport` statement.
py::object np = py::import("numpy");
As an example, let's write the following Python code in C ++ without using py :: exec ()
.
This is a sample to display a graph of y = x ^ 2 with matplotlib.
Python code sample
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10, 10, 0.1)
y = x ** 2
plt.plot(x, y)
plt.show()
Boost above.C using Python++Sample written in
py::object np = py::import("numpy");
py::object plt = py::import("matplotlib.pyplot");
py::object x = np.attr("arange")(-10, 10, 0.1);
py::object y = x * x;
plt.attr("plot")(x, y);
plt.attr("show")();
(Since x ** 2
cannot be written as it is on the C ++ side, it is set to x * x
instead.)
When executed, the graph will be displayed in matplotlib as shown below.
py :: extract ()
, which is not introduced in this article.Recommended Posts