Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Сценарии Lua и объекты 
:(
    Опции темы
Lazin
Дата 2.1.2008, 00:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 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


PM MAIL Skype GTalk   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1084 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.