Встраивание Python в C/C++: первая часть

ОГЛАВЛЕНИЕ

Статья познакомит программистов C/C++ с Python/C API, библиотекой C, помогающей встраивать модули Python в приложения C/C++. Библиотека API предоставляет множество подпрограмм C для инициализации интерпретатора Python, вызова модулей Python и завершения встраивания. Библиотека скомпилирована с Python и распространяется со всеми последними выпусками Python.

• Скачать исходные файлы - 5.23Кб
• Скачать демонстрационный проект - 80.6 Кб

Введение

Статья "Встраивание Python в многопоточные приложения C/C++" (Linux Journal) вдохновила на более глубокое освещение темы встраивания Python. Эта статья написана с двумя целями:
1. Она написана для программистов, более опытных в C/C++, чем в Python. Руководство применяет практический подход и пропускает все теоретические рассуждения.
2. Постараться сохранить межплатформенную совместимость Python при написании кода встраивания.
Имеются модули, написанные другими на Python, которые надо использовать. Вы разбираетесь в C/C++, но мало знакомы с Python. Нет инструмента для преобразования их в код C, как преобразование из FORTRAN. Однако некоторые инструменты генерируют исполнимый модуль из модуля Python. Но они не решают проблему. Преобразование кода в исполнимые модули обычно все усложняет, так как приходится выяснять, как приложение C/C++ взаимодействует с исполнимым "черным ящиком".

Первая часть серии статей рассматривает основы встраивания Python. Вторая часть переходит к более продвинутым темам. Это руководство не обучает языку Python систематически, но кратко описывается, как работает код Python, когда он появляется. Акцент будет на то, как внедрить модули Python в приложения C/C++. Смотрите статью: "Встраивание Python в C/C++: вторая часть".

Чтобы использовать исходный код, надо установить последний выпуск Python, Visual C++ (или компилятор GCC на Linux). Тесты проводились в следующей среде: Python 2.4 (Windows и Linux), Visual C++ 6.0 (Windows) или GCC 3.2 (RedHat 8.0 Linux). В случае Visual C++ выберите конфигурацию выпуска для компиляции. Конфигурация отладки требует библиотеку отладки Python "python24_d.lib", не поставляемую с нормальными сборками.

Справка

Python – мощный интерпретируемый язык, как Java, Perl и PHP. Он обладает множеством прекрасных свойств, ожидаемых программистами, в том числе "простотой" и "переносимостью". Помимо доступных инструментов и библиотек, язык Python хорошо подходит для моделирования. Он бесплатный, и инструменты и библиотеки, написанные для программистов Python, тоже бесплатные. Более подробно о языке читайте на официальном сайте.

Азы встраивания: функции, классы и методы

Сначала рассматривается пример программы на C, вызывающей функцию в модуле Python. Ниже приведен исходный файл "call_function.c":

// call_function.c – пример вызова
// функций python из кода C
//
#include <Python.h>

int main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict, *pFunc, *pValue;

if (argc < 3)
{
printf("Usage: exe_name python_source function_name\n");
return 1;
}

// Инициализировать интерпретатор Python
Py_Initialize();

// Построить объект имени
pName = PyString_FromString(argv[1]);

// Загрузить объект модуля
pModule = PyImport_Import(pName);

// pDict – заимствованная ссылка
pDict = PyModule_GetDict(pModule);

// pFunc – тоже заимствованная ссылка
pFunc = PyDict_GetItemString(pDict, argv[2]);

if (PyCallable_Check(pFunc))
{
PyObject_CallObject(pFunc, NULL);
} else
{
PyErr_Print();
}

// Вернуть ресурсы системе
Py_DECREF(pModule);
Py_DECREF(pName);

// Завершить интерпретатор Python
Py_Finalize();

return 0;
}

Исходный файл Python "py_function.py" выглядит так:

'''py_function.py – исходник Python, служащий для'''
'''демонстрации применения встраивания python'''

def multiply():
c = 12345*6789
print 'The result of 12345 x 6789 :', c
return c

Проверки на действительность объектов пропускаются для краткости. В Windows компилируется исходник C и получается исполнимый модуль по имени "call_function.exe". Для его запуска введите в командную строку "call_function py_function multiply". Второй аргумент является именем файла Python (без расширения), который при загрузке становится именем модуля. Третий аргумент является именем функции Python, вызываемой в модуле. Исходник Python не участвует в компиляции или компоновке; он только загружается и интерпретируется во время выполнения. Вывод из выполнения следующий:

The result of 12345 x 6789 : 83810205

Сам код C не требует разъяснений, не считая того, что:

• Все в Python является объектом. pDict и pFunc являются заимствованными ссылками, поэтому не нужно Py_DECREF() их.
• Все вызовы Py_XXX и PyXXX_XXX являются вызовами Python/C API.
• Код компилируется и выполняется на всех платформах, поддерживаемых Python.

Теперь надо передать аргументы функции Python. Добавляется блок для обработки аргументов для вызова:

if (PyCallable_Check(pFunc)) 
{
// Подготовить список аргументов для вызова
if( argc > 3 )
{
pArgs = PyTuple_New(argc - 3);
for (i = 0; i < argc - 3; i++)
{
pValue = PyInt_FromLong(atoi(argv[i + 3]));
if (!pValue)
{
PyErr_Print();
return 1;
}
PyTuple_SetItem(pArgs, i, pValue);
}

pValue = PyObject_CallObject(pFunc, pArgs);

if (pArgs != NULL)
{
Py_DECREF(pArgs);
}
} else
{
pValue = PyObject_CallObject(pFunc, NULL);
}

if (pValue != NULL)
{
printf("Return of call : %d\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);
}
else
{
PyErr_Print();
}

// некоторый код пропущен...
}

Новый исходник C добавляет блок "Подготовить список аргументов для вызова" и проверку возвращаемого значения. Он создает кортеж (спископодобный тип) для хранения всех параметров для вызова. Выполнение команды "call_function py_source multiply1 6 7" дает вывод:

The result of 6 x 7 : 42
Return of call : 42

Легко писать классы на Python. Также легко использовать класс Python в коде C. Надо лишь создать экземпляр объекта и вызвать его методы, так же как вызываются нормальные функции. Пример ниже:

// call_class.c – пример встраивания python
// (вызов классов python из кода C)
//
#include <Python.h>

int main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict,
*pClass, *pInstance, *pValue;
int i, arg[2];

if (argc < 4)
{
printf(
"Usage: exe_name python_fileclass_name function_name\n");
return 1;
}

// некоторый код пропущен...

// построить имя вызываемого класса
pClass = PyDict_GetItemString(pDict, argv[2]);

// создать экземпляр класса
if (PyCallable_Check(pClass))
{
pInstance = PyObject_CallObject(pClass, NULL);
}

// построить список параметров
if( argc > 4 )
{
for (i = 0; i < argc - 4; i++)
{
arg[i] = atoi(argv[i + 4]);
}
// вызвать метод класса с двумя параметрами
pValue = PyObject_CallMethod(pInstance,
argv[3], "(ii)", arg[0], arg[1]);
} else
{
// вызвать метод класса без параметров
pValue = PyObject_CallMethod(pInstance, argv[3], NULL);
}
if (pValue != NULL)
{
printf("Return of call : %d\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);
}
else
{
PyErr_Print();
}

// некоторый код пропущен...
}

Третий параметр для PyObject_CallMethod(), "(ii)" является форматирующей строкой, указывающей, что следующие аргументы – два целых числа. PyObject_CallMethod() принимает типы переменных C как свои аргументы, а не как объекты Python. Этим он отличается от других вызовов, встречавшихся до сих пор. Исходник Python "py_class.py" выглядит так:

'''py_class.py - исходник Python, служащий для'''
'''демонстрации применения встраивания python'''

class Multiply:
def __init__(self):
self.a = 6
self.b = 5

def multiply(self):
c = self.a*self.b
print 'The result of', self.a, 'x', self.b, ':', c
return c

def multiply2(self, a, b):
c = a*b
print 'The result of', a, 'x', b, ':', c
return c

Для запуска приложения добавляется имя класса между именами модуля и функции, являющейся "Multiply" в данном случае. Командная строка становится "call_class py_class Multiply multiply" или "call_class py_class Multiply multiply2 9 9".