Einbetten von Python in ein C ++ - Programm mit pybind11.
Folgendes kann beim Aufrufen von C ++ - Code aus Python hilfreich sein.
Es scheint, dass pybind11 Python auch über C ++ - Code aufrufen kann, also habe ich es versucht.
Es basiert auf Python3, aber wenn Sie die Funktion der aufzurufenden Python-Bibliothek ändern, funktioniert es auch unter Python2.
pybind11 ist nur Header, kopiert also nur den Header. Fügen Sie danach pybind11 in den Quellcode ein.
#include <pybind11/pybind11.h>
#include <pybind11/eval.h>
Python.h
sollte nicht direkt enthalten sein.
Wie wir später sehen werden, können in einer Windows-Umgebung Probleme auftreten.
Der ursprüngliche Code lautet wie folgt.
#include <pybind11/eval.h>
#include <pybind11/pybind11.h>
#include <iostream>
namespace py = pybind11;
int main(int argc, char** argv) {
wchar_t* program = Py_DecodeLocale(argv[0], nullptr);
if (program == nullptr) {
fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
exit(1);
}
Py_SetProgramName(program);
Py_Initialize();
try {
auto global = py::dict(py::module::import("__main__").attr("__dict__"));
auto local = py::dict();
//Schreiben Sie den Code hier
} catch (py::error_already_set& e) {
std::cout << "Python error.\n" << e.what() << "\n";
}
Py_Finalize();
PyMem_RawFree(program);
return 0;
}
pybind11 löst eine py :: error_already_set
Ausnahme aus, wenn der Python-Code fehlschlägt.
Ich versuche die Ausnahme abzufangen und den Fehler anzuzeigen.
Beachten Sie, dass die Destruktoren für die Variablen "global" und "local" ausgeführt werden müssen, bevor "Py_Finalize" aufgerufen wird.
Führen Sie den unten beschriebenen Code aus. https://docs.python.org/3/extending/embedding.html#very-high-level-embedding
py::eval<py::eval_single_statement>("from time import time,ctime", global, local);
py::eval("print('Today is', ctime(time()))", global, local);
Einzeilige Python-Anweisungen verwenden py :: eval_single_statement
.
Der Ausdruck verwendet py :: eval_expr
. (Standard)
local["x"] = 100;
py::eval<py::eval_single_statement>("y = 200", global, local);
py::eval("print('x + y =', x + y)", global, local); // x + y = 300
Weisen Sie den Wert einfach mit dem Variablennamen als Schlüssel zu.
py::eval<py::eval_single_statement>("z = x + y", global, local);
auto z = local["z"].cast<int>();
std::cout << "cpp: z = " << z << "\n"; // cpp: z = 300
Holen Sie sich den Wert mit dem Variablennamen als Schlüssel und rufen Sie "cast" für den Typ auf, den Sie konvertieren möchten.
py::eval<py::eval_statements>(
"def func_01():\n"
" print('func_01: call')\n",
global, local);
auto func_01 = local["func_01"];
func_01(); // func_01: call
Holen Sie sich das Funktionsobjekt mit dem Funktionsnamen als Schlüssel und rufen Sie es mit operator ()
auf.
Es gibt auch eine Funktion namens "call ()", aber es scheint, dass ihre Verwendung nicht empfohlen wird.
py::eval<py::eval_statements>(
"def func_02(a, b):\n"
" print('func_02: {} + {} = {}'.format(a, b, a + b))\n",
global, local);
auto func_02 = local["func_02"];
func_02(123, 456); // func_02: 123 + 456 = 579
func_02("abc", "efg"); // func_02: abc + efg = abcdefg
Es ist bequem.
py::eval<py::eval_statements>(
"def func_03(a, b):\n"
" return a + b\n",
global, local);
auto func_03 = local["func_03"];
auto result = func_03(123, 456);
std::cout << "cpp: func_03 result "
<< py::str(result).cast<std::string>() << "\n"; // cpp: func_03 result 579
Angenommen, Sie wissen nicht, was im Rückgabewert enthalten ist, konvertieren Sie ihn mit py :: str
in einen String.
Der obige Link beschreibt, wie Sie ein Modul in pybind11 erstellen und den C ++ - Code aufrufen.
Sie können Module auch in C ++ - Programme einbetten. https://docs.python.org/3/extending/extending.html#the-module-s-method-table-and-initialization-function
Erstellen Sie eine Initialisierungsfunktion, ohne das pybind11-Makro PYBIND11_PLUGIN
zu verwenden.
Die definierte Klasse wird für das Beispiel verwendet, das mit dem C ++ - Programm verknüpft werden soll.
class Job {
public:
std::string GetName() const { return m_name; }
void SetName(const std::string& name) { m_name = name; }
private:
std::string m_name;
};
class Person {
public:
std::string GetName() const { return m_name; }
void SetName(const std::string& name) { m_name = name; }
std::shared_ptr<Job> GetJob() const { return m_job; }
void SetJob(const std::shared_ptr<Job>& job) { m_job = job; }
private:
std::string m_name;
std::shared_ptr<Job> m_job;
};
namespace py = pybind11;
PyMODINIT_FUNC PyInit_sample() {
py::module m("sample", "pybind11 module sample.");
py::class_<Job, std::shared_ptr<Job>> job(m, "Job");
job.def(py::init<>()).def_property("name", &Job::GetName, &Job::SetName);
py::class_<Person, std::shared_ptr<Person>> person(m, "Person");
person.def(py::init<>())
.def_property("name", &Person::GetName, &Person::SetName)
.def_property("job", &Person::GetJob, &Person::SetJob);
return m.ptr();
}
int main(int argc, char** argv) {
wchar_t* program = Py_DecodeLocale(argv[0], nullptr);
if (program == nullptr) {
fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
exit(1);
}
PyImport_AppendInittab("sample03", PyInit_sample03);
Py_SetProgramName(program);
Py_Initialize();
py::module::import("sample03");
Stellen Sie sicher, dass Sie "PyImport_AppendInittab" vor "Py_Initialize" aufrufen.
try {
auto global = py::dict(py::module::import("__main__").attr("__dict__"));
//Erstellen Sie eine Funktion in Python(Name=Hoge, Job=Teacher)
py::eval<py::eval_statements>(
"import sample\n"
"def initialize_person(p):\n"
" job = sample.Job()\n"
" job.name = 'Teacher'\n"
" p.name = 'Hoge'\n"
" p.job = job\n",
global);
{
auto person = std::make_shared<Person>();
global["initialize_person"](person);
std::cout << "Name : " << person->GetName() << "\n"; // Name : Hoge
std::cout << "Job : " << person->GetJob()->GetName() << "\n"; // Job : Teacher
}
//Funktion ändern(Name=Foo, Job=Programmer)
py::eval<py::eval_statements>(
"import sample\n"
"def initialize_person(p):\n"
" job = sample.Job()\n"
" job.name = 'Programmer'\n"
" p.name = 'Foo'\n"
" p.job = job\n",
global);
{
auto person = std::make_shared<Person>();
global["initialize_person"](person);
std::cout << "Name : " << person->GetName() << "\n"; // Name : Foo
std::cout << "Job : " << person->GetJob()->GetName() << "\n"; // Job : Programmer
}
} catch (py::error_already_set& e) {
std::cout << "Python error.\n" << e.what() << "\n";
}
Das Objekt wird auf der C ++ - Seite vorbereitet und der Wert wird auf der Python-Seite festgelegt.
Wenn Sie in einer Windows-Umgebung "#include <python.h>" ausführen, wird die Debug-Version der Bibliothek während des Backbuildings verknüpft. Die Debug-Version muss während der Installation hinzugefügt werden. Außerdem muss in der Debug-Version von Python "_d" zum Modul usw. hinzugefügt werden.
pybind11s <pybind11 / pybind11.h>
ist so eingestellt, dass die Release-Version der Bibliothek auch während eines Debug-Builds verknüpft wird.
Beachten Sie, dass es keinen Sinn macht, zuerst <Python.h> einzuschließen.
CMake hat eine FindPythonLibs, die die Python-Umgebung findet. Wenn Sie dies verwenden, wird in einer Windows-Umgebung die Debug-Version der Bibliothek zum Zeitpunkt der Debug-Erstellung verknüpft. Beachten Sie, dass Sie keine Verknüpfung herstellen können, wenn Sie pybind11 in diesem Status verwenden.
Wenn Sie CMake verwenden, verwenden Sie das mit pybind11 gelieferte PythonLibsNew
oder pybind11Tools
.
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/pybind11/cmake")
set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4)
find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/pybind11/cmake")
include(pybind11Tools)
pybind11Tools
hat pybind11_add_module
, was das Definieren von Modulen erleichtert.
Damit ist die Erweiterung des erstellten Moduls ".pyd".
pybind11_add_module(module_sample module_sample.cpp)
Recommended Posts