Versuchen Sie, Python mit pybind11 in ein C ++ - Programm einzubetten

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.

Python-Umgebung

Es basiert auf Python3, aber wenn Sie die Funktion der aufzurufenden Python-Bibliothek ändern, funktioniert es auch unter Python2.

installieren

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.

Führen Sie Python-Code aus C ++ - Code aus

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.

Einfache Probe

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)

Variablen definieren

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.

Wert erhalten

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.

Funktionsdefinition und Aufruf

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.

Übergeben Sie Argumente an die Funktion

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.

Ruft den Rückgabewert der Funktion ab

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.

Bereiten Sie ein Modul im Programm vor

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

Vorbereitung der Initialisierungsfunktion

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();
}

Registrierung der Initialisierungsfunktion

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.

Zusammenarbeit mit C ++ - Programmen

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.

Hinweise zur Windows-Umgebung

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.

Vorsichtsmaßnahmen bei der Verwendung von CMake in einer Windows-Umgebung

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

Versuchen Sie, Python mit pybind11 in ein C ++ - Programm einzubetten
Versuchen Sie, ein Python-Modul in C-Sprache zu erstellen
Löse ABC163 A ~ C mit Python
ABC166 in Python A ~ C Problem
Löse ABC168 A ~ C mit Python
Löse ABC036 A ~ C mit Python
Löse ABC162 A ~ C mit Python
Löse ABC167 A ~ C mit Python
Löse ABC158 A ~ C mit Python
Löse ABC037 A ~ C mit Python
Beim Schreiben eines Programms in Python
Versuchen Sie, Python in der mit pipenv erstellten Django-Umgebung auszuführen
Betten Sie einen Python-Interpreter mit pybind11 + cmake in eine C ++ - App ein
Spiralbuch in Python! Python mit einem Spiralbuch! (Kapitel 14 ~)
Löse ABC175 A, B, C mit Python
[Python] Ein Programm, das Treppen mit # erstellt
Versuchen Sie, sich mit Python bei qiita anzumelden
Ich habe ein Pay-Management-Programm in Python erstellt!
Versuchen Sie, mit Binärdaten in Python zu arbeiten
Versuchen Sie, ein SYN-Paket in Python zu senden
Versuchen Sie, eine einfache Animation in Python zu zeichnen
Schreiben Sie ein Caesar-Verschlüsselungsprogramm in Python
Lesen Sie die Datei in Python mit einem relativen Pfad aus dem Programm
Versuchen Sie HTML-Scraping mit der Python-Bibliothek
Verwenden von C ++ - Funktionen aus Python mit pybind11
Probieren Sie eine funktionale Programmierpipe in Python aus
Übergeben Sie die Liste von Python an C ++ als Referenz in pybind11
Versuchen Sie, eine Karte mit Python + Cartopy 0.18.0 zu zeichnen
[Python] Holen Sie sich die Dateien mit Python in den Ordner
Berechnen wir das statistische Problem mit Python
Versuchen Sie, mit Python eine Lebenskurve zu zeichnen
Erstellen Sie eine virtuelle Umgebung mit conda in Python
Ein Programm, das doppelte Anweisungen in Python entfernt
Versuchen Sie, in Python einen "Entschlüsselungs" -Code zu erstellen
Einfache Pub / Sub-Programmhinweise in Python
Versuchen Sie, mit Mongo in Python auf dem Mac zu arbeiten
Arbeiten Sie in einer virtuellen Umgebung mit Python virtualenv.
Erstellen Sie eine neue Seite im Zusammenfluss mit Python
Rufen Sie Python-Skripte aus Embedded Python in C ++ / C ++ auf
Versuchen Sie, mit Python eine Diedergruppe zu bilden
Ich habe versucht, ein Python 3-Modul in C hinzuzufügen
Fordern Sie AtCoder (ABC) 164 mit Python heraus! A ~ C Problem
Versuchen Sie, ein FizzBuzz-Problem mit einem Shell-Programm zu erstellen
Ich habe ein Caesar-Kryptografieprogramm in Python erstellt.
Versuchen Sie es mit Python.
Weiter Python in C-Sprache
Versuchen Sie gRPC in Python
C-API in Python 3
Probieren Sie 9 Slices in Python aus
Schreiben Sie ein super einfaches molekulardynamisches Programm in Python
Lassen Sie uns ein Befehls-Standby-Tool mit Python erstellen
Empfangen Sie Wörterbuchdaten von Python-Programmen mit AppleScript
Spielen mit der benutzerlokalen API für künstliche Intelligenz in Python
Versuchen Sie, assoziativen Speicher durch Hop-Field-Netzwerk in Python zu implementieren
Erstellen Sie einen einfachen Slackbot mit einer interaktiven Schaltfläche in Python
Versuchen Sie, in Python nach einem Profil mit einer Million Zeichen zu suchen
Machen Sie mit Python einen Haltepunkt auf der c-Ebene
Ich möchte mit einem Roboter in Python arbeiten.
Vom Kauf eines Computers bis zur Ausführung eines Programms auf Python