Використовуємо Python у своїй програмі
При розробці прикладних програм іноді виникає необхідність надати користувачеві досить гнучку, але просту систему для управління програмою.
Є безліч варіантів реалізації таких систем, але одним із найпростіших є вбудовування у додаток інтерпретатора скриптових мов.
Реалізацією цього варіанта ми сьогодні й займемося. Як скриптова мова була обрана Python через досить великий спектр застосування.
Він кроссплатформенний, існують офіційні версії для Windows, Linux та MacOS. Зрештою, про потужність і потенціал цієї мови може говорити хоча б той факт, що вона використовується в відомій корпорації Google як основна мова програмування.
Качаємо з www.python.org останній реліз інтерпретатора мови та встановлюємо його кудись. Перевіряємо наявність наступних файлів:
Python\libs\python31.lib (або libpython31.a, якщо ви використовуєте gcc)
Якщо цих файлів немає, потрібно завантажити з офіційного сайту вихідники інтерпретатора та розпакувати їх кудись. Створіть проект у будь-який IDE (наприклад, MS VS 2005) і пропишіть в налаштуваннях модулів шляху до папок з цими файлами.
Просте програмка
#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'і. Цим ми й займемося. Для початку необхідно зробити наступне:
- Передаємо параметр функції, написаної на Python-і
- Виконуємо її
- Отримуємо результат
def func(x): if x > 3: return x + 1 elif 1
Функція, що викликає скрипт із функцією 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/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, передавши їй функцію та кортеж аргументів. Тепер організація введення користувачем вихідних даних не становить проблеми.