
Эксперт
   
Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re
Репутация: 41 Всего: 154
|
В этой статье описывается, как можно использовать объекты написанные на языке низкого уровня (С++) в lua скрипте. Для начала следует немного рассказать о возможностях lua. Основная структура данных в языке lua - таблицы. В качестве ключей и значений таблицы, используются переменные, значения которых, типизируются динамически. Это означает что таблица может содержать пары, ключ - значение, разных типов, тоесть таблицы в lua неоднородны. Таблица - единственная структура данных в lua. С помощью таблиц реализуются все остальные структуры данных, а так-же объекты. Например таблица, в качестве ключей которой используются численные значения, является аналогом одномерного массива. Таблица так-же может содержать функции. Код | --пример таблицы ... а это комментарий)) local table = {} table["pi"] = 3.14159 table[1] = "some string" table[3.14159] = "pi"
|
Язык lua позволяет перегружать операции для таблиц и userdata, это делается с помощью мета-таблиц. Мета-таблица связывает события и их обработчики и представляет из себя обычную таблицу, в качестве ключей которой используются строки - имена событий, а в качестве значений - функции - обработчики событий. В свою очередь с каждой мета-таблицей, может быть связана еще одна мета-таблица, если событие не определено в первой мета-таблице, то оно ищется во второй, и так далее... События (ключи таблицы) - могут иметь как произвольные значения, так и предопределенные (всегда начинаются с "__"), например, "__add" - перегружает операцию сложения, "__gc" - вызывается перед удалением сборщиком мусора, ключ "__metatable" может содержать другую мета-таблицу и тд... Синтаксис языка предполагает использование мета-таблиц, для создания объектов, для этого предусмотрен оператор : (двоеточие). При вызове функции из таблицы, с помощью этого оператора, в качестве первого параметра будет неявно передана эта таблица. Теперь о том как это все работает. В языках программирования поддерживающих классы, объекты всегда создаются на основе информации о типах. В lua классов нет, но объекты все-таки можно создавать. Скрипт должен содержать код, для создания экземпляра объекта. Для этого он должен, создать новую таблицу и мета-таблицу, содержащую все методы объекта. Ниже приведен код, котрый позволяет использовать объекты класса PersonInfo, в lua скрипте. Для взаимодействия скрипта с объектами PersonInfo, предназначен класс PersonInfoBinder. Код |
extern "C" { # include "lua.h" # include "lauxlib.h" # include "lualib.h" } #include <stdlib.h> #include <stdio.h> #include <string> #include <conio.h>
class PersonInfo { friend class PersonInfoBinder; protected: std::string name; std::string sname; int age; public: static int ObjCount; PersonInfo(): name(), sname(), age(0) { } //конструктор PersonInfo(const char* n, const char* sn, int a): name(n), sname(sn), age(a) { ObjCount++; }
//деструктор virtual ~PersonInfo() { ObjCount--; }
//метод который мы будем вызывать с помощью Lua virtual void ShowInfo() { printf("PersonInfo %s %s, age %d\n",name.c_str(), sname.c_str(), age); } }; int PersonInfo::ObjCount = 0;
//размещающий оператор new, для создания объекта внутри userdata void* operator new(size_t size, lua_State* L, const char* metatableName) { //создаем userdata на вершине стэка void* ptr = lua_newuserdata(L, size); /* состояние стэка * [-1] - объект userdata */ //кладем на вершину стэка metatable с именем metatableName luaL_getmetatable(L, metatableName); /* состояние стэка * [-1] - metatable * [-2] - объект userdata */ //привязываем metatable к объекту userdata lua_setmetatable(L, -2); return ptr; }
//Класс через который интерпретатор Lua может работать с объектами PersonInfo class PersonInfoBinder { public: typedef PersonInfo* pPersonInfo; //метод возвращает указатель на PersonInfo или NULL static pPersonInfo checkPersonInfo(lua_State* L, int ix) { //если на вершине стэка объект userdata if (lua_isuserdata(L,ix)) { //возвращаем указатель на объект void* udata = luaL_checkudata(L,ix,"PersonInfo"); if (udata != NULL) //если получится :-) return (pPersonInfo)udata; } return NULL; } //Ф-я вызывает метод ShowInfo объекта находящегося на вершине стэка static int Print(lua_State* L) { //получаем указатель на объект pPersonInfo p = checkPersonInfo(L, 1); //и надеемся на удачу p->ShowInfo(); return 0; } //метод создает объект PersonInfo static int New(lua_State* L) { //получаем черезстэк параметры для создания const char* name = lua_tostring(L,1); const char* sname= lua_tostring(L,2); int age = lua_tointeger(L,3); //создаем объект PersonInfo на вершине стэка pPersonInfo p = new (L, "PersonInfo") PersonInfo(name, sname, age); return 1;//сообщаем интерпретатору, что ф-я вернула один параметр } //Деструктор для объекта - функция вызываетс для объекта во время сборки мусора static int GC(lua_State* L) { //единственный параметр - указатель на уничтожаемый объект pPersonInfo p = checkPersonInfo(L, 1); //вызываем деструктор явно, Lua самостоятельно освободит память занимаемую userdata p->~PersonInfo(); return 0; } //Ф-я делает объект типа PersonInfo доступным интерпретатору static int Bind(lua_State* L) { //методы которые содержит метатаблица объекта static const luaL_Reg somebody_meta[] = { {"__gc", &GC}, {0, 0} }; //методы самого объекта static const luaL_Reg somebody_methods[] = { {"New", &New}, {"Print", &Print}, {0, 0} }; //Ф-я создает глобальную переменную (таблицу) PersonInfo, и записывает в нее все методы из somebody_methods //данная таблица будет общей для всех объектов этого типа luaL_register(L,"PersonInfo",somebody_methods); //используем таблицу PersonInfo, в качестве мета-таблицы luaL_newmetatable(L, "PersonInfo"); //создаем еще одну таблицу, на этот раз в стеке, содержащую методы из массива somebody_meta luaL_register(L,NULL,somebody_meta); //metatable.__index = methods lua_pushliteral(L, "__index"); //копируем таблицу методов lua_pushvalue(L, -3); lua_rawset(L, -3); /* metatable.__index = methods */ lua_pushliteral(L, "__metatable"); //копируем таблицу методов lua_pushvalue(L, -3); lua_rawset(L, -3); lua_pop(L, 1); return 1; } };
int main(void) { int status, result; double sum; lua_State *L;
L = lua_open(); luaL_openlibs(L); PersonInfoBinder::Bind(L); status = luaL_loadfile(L, "script.lua"); if (status) { (void)fprintf(stderr, "Syntax error\n"); getch(); exit(1); }
result = lua_pcall(L, 0, LUA_MULTRET, 0); if (result) { fprintf(stdout, "runtime error\n"); getch(); exit(1); }
lua_close(L); printf("Objects: %d", PersonInfo::ObjCount); getch(); return 0; }
|
Сценарий, который этот код выполняет: Код | -- script.lua
-- ввод значений пользователем function read_record(table)
io.write("first name: ") name = io.read() io.write("second name: ") sname = io.read() io.write("age: ") agestr = io.read() age = tonumber(agestr) local person = PersonInfo.New(name, sname, age) table[name..' '..sname] = person end
-- вывод значений таблицы function find_record(table)
name = io.read() if table[name] ~= nil then table[name]:Print() else print("\a\r") end end
-- Удаление значений из таблицы function remove_record(table)
name = io.read() if table[name] ~= nil then table[name] = nil else print("\a\r") end end
-- пустая таблица table = {} command = "" while command ~= "quit" do io.write(">> ") command = io.read() if command == "quit" then break elseif command == "find" then find_record(table) elseif command == "add" then read_record(table) elseif command == "remove" then remove_record(table) else print("\a\r") end end
return 0
|
|