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