Essayez d'incorporer Python dans un programme C ++ avec pybind11

Comment intégrer Python dans un programme C ++ en utilisant pybind11.

Ce qui suit peut être utile pour appeler du code C ++ à partir de Python.

Il semble que pybind11 puisse également appeler Python à partir du code C ++, alors je l'ai essayé.

Environnement Python

Il est basé sur Python3, mais si vous changez la fonction de la bibliothèque Python à appeler, cela fonctionnera également sur Python2.

installer

pybind11 est uniquement en-tête, donc il copie simplement l'en-tête. Après cela, incluez pybind11 dans le code source.

#include <pybind11/pybind11.h>
#include <pybind11/eval.h>

Python.h ne doit pas être inclus directement. Comme nous le verrons plus loin, des problèmes peuvent survenir dans un environnement Windows.

Exécuter du code Python à partir du code C ++

Le code d'origine est le suivant.

#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();
		//Écrivez le code ici
	} catch (py::error_already_set& e) {
		std::cout << "Python error.\n" << e.what() << "\n";
	}

	Py_Finalize();
	PyMem_RawFree(program);

	return 0;
}

pybind11 lève une exception py :: error_already_set lorsque le code Python échoue. J'essaye d'attraper l'exception et d'afficher l'erreur.

Notez que les destructeurs des variables global et local doivent être exécutés avant l'appel de Py_Finalize.

Échantillon simple

Exécutez le code décrit ci-dessous. 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);

Les instructions Python sur une ligne utilisent py :: eval_single_statement. L'expression utilise py :: eval_expr. (Défaut)

Définir des variables

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

Attribuez simplement la valeur en utilisant le nom de la variable comme clé.

Obtenez de la valeur

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

Obtenez la valeur en utilisant le nom de la variable comme clé et appelez cast sur le type que vous voulez convertir.

Définition de la fonction et appel

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

Récupérez l'objet fonction en utilisant le nom de la fonction comme clé et appelez-le avec ʻoperator () . Il existe également une fonction appelée call ()`, mais il semble que son utilisation n'est pas recommandée.

Passer des arguments à la fonction

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

C'est pratique.

Obtenez la valeur de retour de la fonction

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

En supposant que vous ne sachiez pas ce qu'il y a dans la valeur de retour, vous la convertissez en une chaîne avec py :: str.

Préparez un module dans le programme

Le lien ci-dessus détaille comment créer un module dans pybind11 et appeler le code C ++.

Vous pouvez également intégrer des modules dans des programmes C ++. https://docs.python.org/3/extending/extending.html#the-module-s-method-table-and-initialization-function

Préparation de la fonction d'initialisation

Créez une fonction d'initialisation sans utiliser la macro pybind11 PYBIND11_PLUGIN. La classe définie est utilisée pour que l'exemple soit lié au programme C ++.

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

Enregistrement de la fonction d'initialisation

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");

Assurez-vous d'appeler PyImport_AppendInittab avant Py_Initialize.

Coopération avec des programmes C ++

try {
	auto global = py::dict(py::module::import("__main__").attr("__dict__"));

	//Créer une fonction en 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
	}

	//Changer de fonction(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";
}

L'objet est préparé du côté C ++ et la valeur est définie du côté Python.

Notes d'environnement Windows

Si vous faites #include <python.h> dans un environnement Windows, la version de débogage de la bibliothèque sera liée lors de la backbuilding. La version de débogage doit être ajoutée lors de l'installation. De plus, dans la version de débogage de Python, "_d" doit être ajouté au module, etc.

pybind11 <pybind11 / pybind11.h> est défini pour lier la version finale de la bibliothèque même pendant une compilation de débogage. Notez qu'inclure d'abord <Python.h> n'a aucun sens.

Précautions lors de l'utilisation de CMake dans un environnement Windows

CMake a un FindPythonLibs qui trouve l'environnement Python. Si vous l'utilisez, dans un environnement Windows, la version de débogage de la bibliothèque sera liée au moment de la génération de débogage. Notez que si vous utilisez pybind11 dans cet état, vous ne pourrez pas établir de lien.

Si vous utilisez CMake, utilisez PythonLibsNew fourni avec pybind11 ou utilisez 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 a pybind11_add_module, ce qui facilite la définition des modules. Avec cela, l'extension du module créé sera .pyd.

pybind11_add_module(module_sample module_sample.cpp)

Recommended Posts

Essayez d'incorporer Python dans un programme C ++ avec pybind11
Essayez de créer un module Python en langage C
Résoudre ABC163 A ~ C avec Python
ABC166 en Python A ~ C problème
Résoudre ABC168 A ~ C avec Python
Résoudre ABC036 A ~ C avec Python
Résoudre ABC162 A ~ C avec Python
Résoudre ABC167 A ~ C avec Python
Résoudre ABC158 A ~ C avec Python
Résoudre ABC037 A ~ C avec Python
Lors de l'écriture d'un programme en Python
Essayez d'exécuter python dans l'environnement Django créé avec pipenv
Incorporer un interpréteur Python dans une application C ++ avec pybind11 + cmake
Livre en spirale en Python! Python avec un livre en spirale! (Chapitre 14 ~)
Résoudre ABC175 A, B, C avec Python
[Python] Un programme qui crée des escaliers avec #
Essayez de vous connecter à qiita avec Python
J'ai fait un programme de gestion de la paie en Python!
Essayez de travailler avec des données binaires en Python
Essayez d'envoyer un paquet SYN en Python
Essayez de dessiner une animation simple en Python
Ecrire un programme de chiffrement Caesar en Python
Lisez le fichier en Python avec un chemin relatif depuis le programme
Essayez le scraping HTML avec la bibliothèque Python
Utilisation des fonctions C ++ de python avec pybind11
Essayez un tube de programmation fonctionnel en Python
Passer la liste de Python vers C ++ par référence dans pybind11
Essayez de dessiner une carte avec python + cartopy 0.18.0
[Python] Récupérez les fichiers dans le dossier avec Python
Calculons en fait le problème statistique avec Python
Essayez de dessiner une courbe de vie avec python
Créer un environnement virtuel avec conda avec Python
Un programme qui supprime les instructions en double en Python
Essayez de créer un code de "décryptage" en Python
Notes de programme simples Pub / Sub en Python
Essayez de travailler avec Mongo en Python sur Mac
Travaillez dans un environnement virtuel avec Python virtualenv.
Créer une nouvelle page en confluence avec Python
Appel de scripts Python à partir de Python intégré en C ++ / C ++
Essayez de créer un groupe de dièdre avec Python
J'ai essayé d'ajouter un module Python 3 en C
Défiez AtCoder (ABC) 164 avec Python! Un problème ~ C
Essayez de créer un problème FizzBuzz avec un programme shell
J'ai créé un programme cryptographique César en Python.
Essayez de gratter avec Python.
Next Python en langage C
Essayez gRPC en Python
API C en Python 3
Essayez 9 tranches en Python
Ecrire un programme de dynamique moléculaire super simple en python
Faisons un outil de veille de commande avec python
Recevez des données de dictionnaire à partir de programmes Python avec AppleScript
Jouer avec l'API d'intelligence artificielle locale de l'utilisateur en Python
Essayez d'implémenter la mémoire associative par hop field network en Python
Créez un Slackbot simple avec un bouton interactif en python
Essayez de rechercher un profil d'un million de caractères en Python
Faire un point d'arrêt sur la couche c avec python
Je veux travailler avec un robot en python.
De l'achat d'un ordinateur à l'exécution d'un programme sur python