#cpp #python #linux #boost #python_c_api
Допустим, что объявлен простой класс на языке Python в файле 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) Как вызвать метод print_position из кода написанного на C++? main.cpp: #include#include int main() { Py_Initialize(); // ... CODE HERE Py_Finalize(); } Смотрел в сторону Cyhton и Boost/Python, но точно разобраться не получилось. Ссылки в которых что-то нашел: Первая ссылка Вторая ссылка
Ответы
Ответ 1
Отсюда (с небольшими правками под Python 3.5 и дополнительными проверками) #includeint 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; } Как заметил @jfs, это только пример, код сильно упрощён для ясности. В боевом проекте на те же операции будет раза в два больше кода. Или в четыре, если понадобится многопоточность. Ответ 2
При ответе на вопрос очень помогли ответы выше и ответы по ссылке. Структура проекта следующая: в одной папке лежат файлы main.cpp и example.py. Файл 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: #include#include #include //#include 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; } Для запуска под Ubuntu в консоли необходимо ввести команды: $ g++ main.cpp -std=c++11 -lpython2.7 $ ./a.out example P.S. аргумент командной строки совпадает с названием импортируемого и написанного мною Python модуля.
Комментариев нет:
Отправить комментарий