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é.
Il est basé sur Python3, mais si vous changez la fonction de la bibliothèque Python à appeler, cela fonctionnera également sur Python2.
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.
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
.
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)
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é.
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.
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.
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.
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
.
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
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();
}
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
.
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.
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.
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