При разработке прикладных программ часто возникает необходимость предоставить пользователю какую-то очень гибкую систему для управления программой. Есть множество вариантов реализации таких систем, но одним из наиболее простых является встраивание в приложение интерпретатора скриптовых языков. Реализацией этого варианта мы сегодня и займемся. В качестве скриптового языка был выбран Python из-за обширного спектра применения. На нем можно писать все: от баз данных и сетевых приложений до мелких утилит и игр. Он кроссплатформенный: сейчас существуют официальные версии для Windows, Linux и MacOS.  В конце концов, о мощи и потенциале этого языка может говорить хотя бы тот факт, что он используется в небезызвестной корпорации Google как основной язык программирования.

Подготовка

Для начала нужно скачать с http://www.python.org последний релиз интерпретатора языка (на момент написания статьи последней была версия 3.1.1) и установить его куда-нибудь. Проверьте наличие следующих файлов:

C:\Python\include\Python.h

C:\Python\libs\python31.lib (либо libpython31.a, если вы используете gcc)

Если этих файлов нет, нужно скачать с официального сайта исходники интерпретатора и распаковать их куда-нибудь. Создайте проект в любой IDE (например, MS VS 2008) и пропишите в настройках подключаемых модулей пути к папкам с этими файлами.

Простая программа

Скопируйте нижеследующую программку себе в проект:

#include

int main(int argc, char *argv[])
{
  Py_Initialize(); // инициализация интерпретатора
  PyRun_SimpleString("from time import time,ctime\n"
                     "print('Today is', ctime(time()))\n");
    // выполняется нужный скрипт
  Py_Finalize(); // очищение памяти, отданной интерпретатору
  return 0;
}

Давайте ее обсудим. Первым делом подключается Python.h для получения доступа к Python API. Перед работой с интерпретатором необходимо обязательно вызывать Py_Initialize, после ее окончания Py_Finalize.  Один из способов вызвать на выполнение нужный скрипт – это функция PyRun_SimpleString. Она принимает в качестве параметра строку со скриптом и выполняет. В приведенном примере функция выведет на экран текущее время. Однако в большинстве случаев этого недостаточно.

Получение результата выполнения функции

Предположим, что пишется программа для построения графиков математических функций. Если это простая функция вроде у = x + 1 ― cos(x), то проблем с вводом не возникнет, достаточно написать простой парсер выражений на основе обратной польской записи. Но функций могут быть и более сложные, например:

Тут уже с организацией пользовательского ввода имеются проблемы. Но можно дать возможность пользователю написать данную функцию на скриптовом языке. Например, на том же Python’е. Этим мы и займемся. Для начала, необходимо сделать следующее:

  1. Передать параметр функции, написанной на Python’е
  2. Выполнить ее
  3. Получить результат

Вышеприведенная функция y(x) на Python’е записывается так:

def func(x):
    if x > 3:
	return x + 1
    elif 1 < x <= 3:
	return 0
    else:
   	return x ** x

Функция, вызывающая скрипт с функцией y(x) из модуля:

double compute(double x)
{
 PyObject *pName, *pModule, *pFunc;
 PyObject *pArgs, *pValue;
 double result = 0.0;
 Py_Initialize();
 pName = PyUnicode_FromString("mod");
 pModule = PyImport_Import(pName);
 Py_DECREF(pName);
 if (pModule != NULL)
 {
    pFunc = PyObject_GetAttrString(pModule, "func");
    if (pFunc && PyCallable_Check(pFunc))
    {
       pArgs = PyTuple_New(1);
       PyTuple_SetItem(pArgs, 0, PyFloat_FromDouble(x));
       pValue = PyObject_CallObject(pFunc, pArgs);
       Py_DECREF(pArgs);
       if (pValue != NULL)
       {
          result = PyFloat_AsDouble(pValue);
          Py_DECREF(pValue);
       }
   }
   Py_XDECREF(pFunc);
   Py_DECREF(pModule);
  }
 Py_Finalize();
 return result;
}

Обсудим использованные функции, не особо вдаваясь в подробности, т. к. цель данной статьи состоит в рассказе об основных моментах встраивания Python’а в приложения. Для получения более подробных сведений можно почитать документацию на официальном сайте.

Функции вида Py*_From* позволяют преобразовывать встроенные типы С/C++ во внутренние типы Python’а. Т. е., например,  PyUnicode_FromString преобразует свой строковый параметр во внутреннее юникодовое представление Python’а для того, чтобы интерпретатор языка смог потом с ним (с параметром) работать. Функции вида Py*_As* имеют обратное действие (т.е. преобразовывают типы из внутреннего представления интерпретатора во встроенные типы C/C++).

PyImport_Import позволяет получить доступ к функциям модуля, имя которого указывается в качестве параметра. Тип параметра, разумеется, нужно предварительно преобразовать во внутреннее представление.

Py_DECREF ― макрос, высвобождающий память, занятую используемыми интерпретатором данными. Этот макрос отличается от Py_XDECREF тем, что последний может принимать нулевые значения в качестве параметра. Например, следующий фрагмент преобразует строку «mod» во внутренний тип Python’а и загружает нужный модуль.

pName = PyUnicode_FromString("mod");
pModule = PyImport_Import(pName);
Py_DECREF(pName);

После этого объект, содержащий строку, более не нужен, поэтому память очищается при помощи Py_DECREF.

PyObject_GetAttrString возвращает указатель на функцию из модуля, указанного в качестве первого параметра с именем, указанным в качестве второго.

Для передачи параметров функциям используются так называемые «tuples». Далее для простоты будем называть их кортежами. Кортежи – наборы определенного количества значений разных типов. Для создания нового кортежа используется функция PyTuple_New  с указанием нужного числа передаваемых аргументов. Потом кортеж заполняется значениями при помощи PyTuple_SetItem, первый аргумент которой – кортеж, второй – порядковый номер аргумента, а третий – его значение. После этого можно вызвать функцию PyObject_CallObject, передав ей функцию и кортеж аргументов.
Теперь организация ввода пользователем исходных данных не составляет проблемы.

Заключение

Все возможности интерпретатора Python’а при встраивании его в приложения здесь раскрыты не были. Но данная статья может стать хорошим стартом для их изучения, и в этом вам поможет документация на http://www.python.org.

Извините, комментарии отсутствуют.

Вы должны войти для того, чтобы оставить комментарий.