Сейчас практически почти каждый язык программирования имеет встроенную поддержку ООП, С++ имеет, а 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
Комментариев нет:
Отправить комментарий