Введение в Lua


Стек LUA


Данные передаются из lua в C/C++ и обратно через lua стек. Это, по существу, канал связи. Когда я вызываю функцию, функция помещается в стек, затем первый параметр так же помещается в стек и так далее. Это не традиционный стек, единственные операции доступные со стеком - положить и извлечь. Эти функции описывают всю специфику доступа к элементам стека. Как упомянуто ранее, это не совсем стек, но сейчас мы рассматриваем его именно так. Все операции доступа к элементам стека осуществляются по индексам. Значение которого может быть как положительным так и отрицательным. Вот что говорится в документации lua об этом:

Положительный индекс представляет абсолютную позицию в стеке (начинающуюся с 1, а не 0 как в C); отрицательный индекс представляет смещение от вершины стека. То есть, если стек имеет n элементов, индекс 1 представляет первый элемент (первый элемент, помещенный в стек), индекс n представляет последний элемент; индекс -1 также представляет последний элемент (то есть верхний элемент), и индекс -n представляет первый элемент. Мы говорим, что индекс допустимый, если он лежит между 1 и вершиной стека (то есть (1 <= abs(индекс) <= вершина стека)).

Также замечу, что есть несколько дополнительных функций, которые сгруппированы как lua_toXXX, используемые для обращения к стеку lua. Исследуйте документацию к lua для справки. В следующих примерах, я буду копаться в них. В общем, о чем я? А, да, склеивающие функции …

Если Вы взгляните на мою склеивающую функцию l_addNPC:

int l_addNPC( lua_State* luaVM) { theNPCManager->AddNPC( lua_tostring(luaVM, -1) ); lua_pushnumber( luaVM, 0 ); return 1; }

По порядку, что я делаю: вызываю AddNPC метод менеджера NPCManager. Как параметр я передаю строку, которая в настоящее время является последним элементом в стеке lua. Сейчас AddNPC  ничего не делает, я считаю, что все проходит нормально и помещаю число обратно в стек lua, по существу это возвращаемое значение. Точно так же в DeleteNPC.

OK, теперь мы имеем склеивающую функции.
Далее, как объяснить lua, что такие функции существуют? И как объяснить, сколько параметров они требуют. Внимание, приготовьтесь. С помощью lua Вы можете послать сколько угодно параметров. Также, любая функция в lua может вернуть больше чем один результат. Это действительно так. Любая lua или доступная lua функция может вернуть множество значений. Это называется Кортеж [Tuple]. Это круто, но вначале немного смущает. Так, что насчет связывания lua и C/C++? Это не сложно. Конкретно, что мы должны сделать - зарегистрировать функцию в lua. Lua функция lua_register обеспечивает нас такими функциональными возможностями.
lua_register(L, n, f) где
L: lua_State, то где мы регистрируем функцию
N: символьное имя функции передающееся lua
F: склеивающая функция.

Интересно, что lua_register() не функция, а макрокоманда. Вот, что она из себя представляет:
(lua_pushcfunction(L, f), lua_setglobal(L, n))

lua_pushcfunction помещает C функцию в lua_State. Также, имя этой функции (n) добавляется в 'глобальное' [global] пространство имен функций. Теперь, когда нам потребуется функция в lua мы используем это имя, которое будет связано с нашей функцией.

С помощью макрокоманды lua_register, мы предоставили lua две функции с именами addNPC  и deleteNPC. Теперь я могу использовать их в любом скрипте lua. Итак, основываясь на предыдущем примере, если Вы исследуете main.cpp, то заметите такие изменения:

int main(int argc, char* argv[]) { lua_State* luaVM = lua_open(0);

if (NULL == luaVM) { printf("Error Initializing lua\n"); return -1; }

// инициализация стандартных библиотечных функции



lua_baselibopen(luaVM); lua_iolibopen(luaVM); lua_strlibopen(luaVM); lua_mathlibopen(luaVM);

printf("Simple Functional lua interpreter\n"); printf("Based on lua version 4.0.1\n"); printf("Registering Custom C++ Functions.\n"); lua_register( luaVM, "addNPC", l_addNPC ); lua_register( luaVM, "deleteNPC", l_deleteNPC );

printf("Enter lua commands. type 'exit' to exit\n");

printf("\n>"); lua_dofile(luaVM, "./startup.lua");

// Вывод NPC которые были добавлены.

theNPCManager->Dump();

lua_close(luaVM);

return 0; }
<


Далее, какие изменения в lua? Хм, это очень просто:
-- Простейший lua скрипт -- комментарий добавляется как '--' -- Новые C функции доступны из lua -- addNPC("NPC Name") -- deleteNPC("NPC Name")

addNPC("Joe"); addNPC("Sue"); addNPC("KillBot2000");

addNPC("BotToDelete"); addNPC("Krista"); addNPC("Brandon"); deleteNPC("BotToDelete");
Выполнение кода произведет следующий результат:

Я могу добавлять и удалять NPC в моей системе. Отображение результатов является частью движка, а не lua скрипта.

Теперь я могу изменить функциональные возможности моего приложения просто изменяя скрипт. И мне больше не нужно перекомпилировать строки движка. Если мне потребуется использовать luac компилятор, я это сделаю. Я включил откомпилированную версию 'startup.lua' с именем 'startup.lub'. Измените main.cpp для загрузки этого файла и заработаете дополнительный бонус.


Содержание раздела