Good day.

Assume that a simple python class is declared in the example.py file.

example.py

 class point: def __init__(self, x, y): self.x = x self.y = y def print_position(self): return "X: {0} Y: {1}\n".format(self.x, self.y) 

How to call the print_position method from code written in C++ ?

main.cpp

 #include<python2.7/Python.h> #include <boost/python.hpp> int main() { Py_Initialize(); ... CODE HERE Py_Finalize(); } 

I looked in the direction of Cyhton and boost.python but I couldn’t figure it out. Links in which I found something:

  • @jfs as I understood, it is possible to create std::string into which to transfer all the commands necessary for execution in python and then just execute it using PyRun_SimpleString( command for execution) , but I would not like to use this solution. Ideally, I would like this scheme: std::vector<int> a = {1,2,3} declared in a C++ file, then create an object of a class that is declared in the python module and that performs a lot of complex manipulations with this vector, execute them and return the result all in the same C++ module. - Zakharov Aleksey
  • you decide what you want: expand Python or embed Python (opposite things); Call the code that the object from the Python class creates, or vice versa from the Python call the C ++ code that the C ++ classes can use or you want to wrap the C ++ class and provide an interface for Python. It is possible to embed and expand Python simultaneously if necessary - jfs
  • @jfs I want to embed python in C++ . I want within C++ code to create an object of a class that is declared in the python module, to execute some methods of this class with this object declared in the same python module and get the result. (All this is inside C++ code) - Zakharov Aleksey
  • one
    from a C ++ point of view, all Python objects are pointers to PyObject. You can import a module, get a class in it, call this class to get an object, get attributes of this object (for example, some method), call this method - it will be extremely verbose through Python C API, it is easier to write on Python : import module; module.Klass(1).method("a", 2) import module; module.Klass(1).method("a", 2) . It is usually better to extend Python than to build in (unless you create scripting capabilities for others like in Blender, Maya, Open Office). - jfs

2 answers 2

From here (with minor edits under Python 3.5 and additional checks)

 #include <Python.h> int main(int argc, char *argv[]) { PyObject *pName, *pModule, *pDict, *pClass, *pInstance, *pValue; int i, arg[8]; if (argc < 4) { fprintf(stderr, "Usage: call module_name class_name method_name [arguments]\n"); return 1; } Py_Initialize(); // Преобразуем первый аргумент в unicode-строку python pName = PyUnicode_DecodeFSDefault(argv[1]); // Импортируем модуль // import module_name pModule = PyImport_Import(pName); Py_DECREF(pName); // Освобождаем ссылку на строку с именем модуля if (pModule != NULL) { // Получаем пространство имён (__dict__) модуля pDict = PyModule_GetDict(pModule); // Получаем класс class_name pClass = PyDict_GetItemString(pDict, argv[2]); // Проверяем, что полученный class_name можно вызвать if (pClass && PyCallable_Check(pClass)) { // Получаем объект // obj = class_name() pInstance = PyObject_CallObject(pClass, NULL); if(pInstance != NULL) { // Подготавливаем параметры if(argc > 4) { for (i = 0; i < argc - 4; i++) { arg[i] = atoi(argv[i + 4]); } // Вызываем метод с двумя параметрами // value = obj.multiply2(3, 2) pValue = PyObject_CallMethod(pInstance, argv[3], "(ii)", arg[0], arg[1]); } else { // Вызываем метод без параметров // value = obj.multiply() pValue = PyObject_CallMethod(pInstance, argv[3], NULL); } if (pValue != NULL) { printf("Return of call : %d\n", PyLong_AsLong(pValue)); // Освобождаем ссылку на выделенную в памяти // переменную для возвращённого из метода результата Py_DECREF(pValue); } else { PyErr_Print(); } Py_DECREF(pInstance); // Освобождаем ссылку на объект } else { PyErr_Print(); } Py_DECREF(pClass); // Освобождаем ссылку на класс } else { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find class \"%s\"\n", argv[2]); } Py_DECREF(pDict); // Освобождаем ссылку на пространство имён Py_DECREF(pModule); // Освобождаем ссылку на модуль } else { PyErr_Print(); } Py_Finalize(); return 0; } 

As @jfs noted, this is just an example, the code is greatly simplified for clarity. In a combat project, the same operations will be twice as large as the code. Or four, if you need multithreading.

  • for clarity, we can mention that it does: import module_name; module_name.class_name().method_name(*args) import module_name; module_name.class_name().method_name(*args) , which the verbosity of C code demonstrates, despite the lack of error handling, GIL processing, and other useful things. - jfs
  • @jfs plentifully commented on the code. - Sergey Gornostaev
  • @jfs thanks for the comments, they really helped me figure it out. - Zakharov Aleksey
  • @SergeyGornostaev thank you very much for the answer - it helped me figure it out. - Zakharov Aleksey

Answering the question greatly helped the answers above and the answers to the link .

The project structure is as follows: the main.cpp and example.py files are in the same folder.

File example.py

 class point: def __init__(self, x, y): self.x = x self.y = y def print_position(self): return "X: {0} Y: {1}\n".format(self.x, self.y) 

main.cpp file:

 #include <iostream> #include <cstring> #include <python2.7/Python.h> //#include <boost/python.hpp> int main(int argc, char* argv[]) { Py_Initialize(); // Для импорта созданного мною модуля необходимо передать // его имя как аргумент командной строки (у меня example.py) PySys_SetArgv(argc, argv); PyObject* module_name = PyString_FromString("example"); // Загрузка модуля (example.py) PyObject* module = PyImport_Import(module_name); if (module == nullptr) { PyErr_Print(); std::cerr << "Failed to import module\n"; return 1; } // Загрузка пространств имен модуля PyObject* dict = PyModule_GetDict(module); if (dict == nullptr) { PyErr_Print(); std::cerr << "Failed to import __dict__\n"; return 1; } // Загрузка класса из example.py std::string py_class_name = "point"; PyObject* py_class = PyDict_GetItemString(dict, py_class_name.c_str()); if(py_class == nullptr) { PyErr_Print(); std::cerr << "Failed import class " << py_class_name << std::endl; return 1; } // Поскольку конструктор принимает пару аргументов // То необходимо создать пару аргументов PyObject* py_arg_tuple = PyTuple_New(2); PyTuple_SetItem(py_arg_tuple, 0, PyInt_FromLong(5)); PyTuple_SetItem(py_arg_tuple, 1, PyInt_FromLong(10)); // Создание объекта класса point PyObject* obj; if (PyCallable_Check(py_class)) obj = PyObject_CallObject(py_class, py_arg_tuple); else std::cout << "Cannot instantiate the Python class" << std::endl; // Вызываем метод print_position класса point PyObject* val = PyObject_CallMethod(obj, "print_position", NULL); if (!val) PyErr_Print(); // конвертируем результат в std::string и печатаем std::string s (PyString_AsString(val)); std::cout << s; Py_Finalize(); return 0; } 

To run under ubuntu, enter the following commands in the console:

 $ g++ main.cpp -std=c++11 -lpython2.7 $ ./a.out example 

PS command line argument is the same as the name of the python module being imported and written by me.