суббота, 23 апреля 2011 г.

Внедрение IronPython. Основы.

Программирование развивается очень активно, программы становятся все сложнее и сложнее и современные программисты все чаще и чаще обращаются к некой концепции, которую я хотел бы назвать концепцией "динамического кода". Давайте разовьем эту тему далее и обсудим все важные концепции.

В современных достаточно объемных программах статический скомпилированный код может оказаться недостаточно гибок для решения какой либо задачи, ведь статичность-это всегда статичность,никого вмешательства, только мертвый двоичный код, неизменный после компиляции программы. Расшатать его, внести нечто новое иное - вот что действительно вызывает интерес у программистов в наше время. Представьте себе - налету программа сама генерирует себя, вносит в себя всевозможные изменения и приобретает новые свойства!

Быть может, не все пока еще осознают преимущество данной концепции) Давайте рассмотрим пример программы, задача реализации которой может встать перед программистом.

Задача: В зависимости от каких либо внешних источников создать несколько экземпляров класса, записать данные и потом пользоваться ими.

Пояснение: То есть необходимо где то прочитать набор инструкций и поступить в соответствии с ними.


Пример простой реализации: Давайте разберем небольшой пример кода,как можно сделать решить данную задачу.

У нас есть файл,содержащий набор строк следующего вида

Бони,23,181,56778908
Игги,57,170,55432330

Заранее мы знаем,что здесь идет перечисление характеристик. Не представляется сложным счесть строки из файла в массив строк и обработать каждую строку (разбить на части по запятым, и каждую лексему записать как значение соответствующей переменной).

string[] fileStrings = File.ReadAllLines("data.txt");    // data.txt - наш файл с двумя строками
Person[] person = new Person[fileStrings.Lenght];   //создаем массив экземпляров класса Person
for(int i = 0; i < fileStrings.Lenght; i++)
{
     string[] tokens = fileStrings[i].Split(new char[]{','});  // получаем массив лексем
     person[i] = new Person();  // создаем экземпляр
     person[i].Name = tokens[0];    // забиваем каждое поле нашим токеном, предварительно  //конвертируя его в нужный тип(приводя к нужному типу)
     person[i].Age = int.Parse(tokens[1]);
     person[i].Tall = int.Parse(tokens[2]);
     person[i].Number = int.Parse(tokens[3]);
}

Как вы могли бы заметить, данный способ очень тривиальный, но не совсем гибкий. Достаточно поменять местами записи в файле и весь алгоритм собьется. Бедному Игги будет 170 лет,а то еще и 55432330 =)

Конечно, все в основном будут смотреть в сторону XML,что и правильно. Там все четко, параметры и привязанные к ним значения. Если параметров 100 - писать будете код долго,нудно и не интересно.Хочется гибкости. Динамичности. Как если бы на лету компилировалась программа,опираясь на внешние источники в виде текстовых файлов.....)

Конечно, пример,приведенный выше-не самый яркий пример острой потребности в "динамическом коде", но такая проблема тоже может встать и при помощи этого метода вполне просто решится)

Динамический код нужен тогда,когда хорошо бы было на лету сгенерировать классы, не обращаясь к рефлексии, создать динамически экземпляры классов с именами,которых нет в коде исходной программы вообще, выполнить какие нибудь инструкции, которые человек захочет поменять (а двоичный код программы поменять невозможно). Вот тогда то и мы подходим вплотную в нашему вопросу.

Встроенный интерпретатор, способный напрямую взаимодействовать с объектами кода C#. Свой язык программирования в вашей программе. Продвинутый редактор скриптов в движке игры. Динамический компилируемый исполняемый код, тесно взаимодействующий с вашей программой, ее полями, методами, классами, пространствами имен. Все это-концепция динамического кода.

В наше время есть множество наработок, связанных с различными встраиваемыми языками программирования. Я хочу рассмотреть наиболее приглянувшийся и развивающийся комплекс модулей под названием IronPython. IronPython - динамически компилируемый код, взаимодействующий с CLR, работающий в среде .NET Framework и способный к полному взаимодействию с ней. По сути-вариация языка Python, любимого веб-программистами.
Более подробно с его описанием можно ознакомится на официальном сайте http://ironpython.net/
Там же есть и официальная документация на английском языке.

Для работы с ним нам понадобится скачать исходники IronPython и взять необходимые нам dll.
Исходники находятся здесь http://ironpython.codeplex.com/releases/view/54498#DownloadId=216705 (если вы используете Visual Studio 2008, то вам нужна более старая версия,а именно http://ironpython.codeplex.com/releases/view/12482#DownloadId=96607 )

После скачивания и распаковки в корневом каталоге будут содержаться все необходимые нам модули

IronPython.dll
IronPython.Modules.dll
Microsoft.Scripting.dll
Microsoft.Scripting.Debugging.dll

Их будет необходимо добавить в наш проект ( Project-Add Reference...)


Далее необходимо внести в директивы using использование соответствующих пространств имен

using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

Предварительно мы подготовили почву для работы. Далее необходимо будет объявить несколько экземпляров классов для работы с компилятором-интерпретатором. Этим мы и займемся далее. Для удобства я предлагаю сделать наше приложение консольным и оперировать в нем. Объявим набор экземпляров и рассмотрим,за что каждый из них отвечает:

ScriptRuntimeSetup setup = Python.CreateRuntimeSetup(null);
ScriptRuntime runtime = new ScriptRuntime(setup);
runtime.LoadAssembly(System.Reflection.Assembly.GetExecutingAssembly());
ScriptEngine engine = Python.GetEngine(runtime);
ScriptScope scope = engine.CreateScope();

здесь поле setup - это просто класс,хранящий в себе набор автоматически генерируемых настроек. Они будут применены при создании объекта runtime. Этот объект следует рассмотреть более подробно. Абстрактно-объект runtime содержит некие параметры той среды, где он будет работать и выполнять свой код (набор различных настроек и параметров). Ему необходимо передать в качестве сборки (assembly) то приложение, которое работает в данный момент (для людей,знакомых с рефлексией,это откроет безграничный простор для фантазии-с динамическими генерируемыми сборками развернутся есть куда). После этого мы создаем центр и сердце нашего интерпретатора - движок (engine). Именно он будет выполнять весь наш код. После его создания (как было оговорено выше,в качестве параметров передается исполняющая среда(runtime)). Остается только создать объект scope - именно он в будущем будет передаваться в качестве параметра в метод,отвкчающий за компиляцию. Зачем он нужен? На бытовом уровне к нему можно отнестись, как к некой связи компилируемого кода с нашей исходной программой, написанной на C#. scope - мост между кодом на Python и программой на C#.

После всех предварительных подготовок реализуем некоторое весьма простое ядро запуска кода:

     while (true)
            {
                string script = Console.ReadLine();
                try
                {
                    engine.Execute(script, scope);
                }
                catch (Exception exc)
                {
                    Console.WriteLine(exc.Message);
                }
            }

В этом коде даже реализована некая поддержка обработки исключений и вывода текста ошибки,что может оказаться весьма полезно. Здесь все тривиально. Считываем строчку, запускаем метод Execute, в который передаем строчку с кодом и scope - связь между программой на Python и кодом на C#. Добавляйте всякие украшательства и получите некую программу, которую написал я:


тут и обработка исключения компилятора и работоспособный код,который успешно выполнился.

На этом пока все. В следующих статьях мы рассмотрим немного синтаксиса языка Python, прикладное применение для решения конкретных задач, а также некие базовые возможности, которые могут понадобится.

До встречи и удачи вам в нелегком труде!

Комментариев нет:

Отправить комментарий