Вызов методов экземпляра объекта из функции LUA
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 07.02.09 09:04
Оценка:
Приветствую!

Ковыряю LUA, хочется в скрипте на LUA сделать подобное:

function call_object_method(obj)
  obj.MyMethod("Hello from LUA!");
end


Имею солидный опыт использования ActiveScript в программах на С++, там подобные вещи делаются реализацией интерфейса IDispatch для объекта, и в методе Invoke уже можно разбирать, какой метод вызван и действовать соответственно.

Какими путями сделать подобное в LUA? Сразу скажу, что регистрировать глобальный объект в LUA не хочется, т.к. некошерно это. На мой взгляд гораздо правильней иметь функцию (например, по обсчету состояния NPC), в которую будет передаваться экземпляр класса NPC, а из скрипта уже будут дергаться его методы при необходимости.

Сильно подозреваю, что реализация данного вопроса как-то связана с метатаблицами, однако многочасовое копание в документации пока понимания не принесло. Помогите плз наступить на правильные грабли

З.Ы. Использовать tolua и luabind не предлагать, т.к. цель стоит совершенно очевидная — разобраться в кишочках того, как это все должно правильно работать. Заранее благодарен.
Re: Вызов методов экземпляра объекта из функции LUA
От: z00n  
Дата: 07.02.09 12:36
Оценка:
Здравствуйте, Flamer, Вы писали:
F>Ковыряю LUA, хочется в скрипте на LUA сделать подобное:
F>
F>function call_object_method(obj)
F>  obj.MyMethod("Hello from LUA!");
F>end
F>


Это не проблема, только синтакс вызова метода в луа: obj:MyMethod("...")

F>Имею солидный опыт использования ActiveScript в программах на С++, там подобные вещи делаются реализацией интерфейса IDispatch для объекта, и в методе Invoke уже можно разбирать, какой метод вызван и действовать соответственно.

F>Какими путями сделать подобное в LUA? Сразу скажу, что регистрировать глобальный объект в LUA не хочется, т.к. некошерно это. На мой взгляд гораздо правильней иметь функцию (например, по обсчету состояния NPC), в которую будет передаваться экземпляр класса NPC, а из скрипта уже будут дергаться его методы при необходимости.

Для начала прочитайте Programming in Lua — ch.28: User-Defined Types in C (имейте в виду, что код 5.0.x)

Самый хороший и полный обзор метов связывания(от авторов луа):
Game Programming Games 6: Binding C/C++ objects to Lua by W. Celes, Luiz H. de Figueiredo, R. Ierusalismchy
Упрощенный пример кода можно посмотреть тут.
Где взять GPG6? :)
Re[2]: Вызов методов экземпляра объекта из функции LUA
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 07.02.09 13:11
Оценка:
Здравствуйте, z00n, Вы писали:

Z>Это не проблема, только синтакс вызова метода в луа: obj:MyMethod("...")


Спасибо за ответ, дело в том, что с LUA пока опыта мало. Единственное, чего пока добился — это криво регистрировать объект как имеющий методы. Ниже небольшой пример того, что есть, но он не совсем правильно работает. Итак:

Скрипт LUA:
function object_test()
    alert(obj.GetMessage());
end


Мой дикий класс для динамического связывания (там костыли, просьба воспринимать это как тест):
class CLuaObjectBinder
{
private:
    typedef struct
    {
        CLuaObjectBinder* _this;
        std::string MethodName;
    } CThisData;

    std::vector<CThisData> m_Methods; // methods to bind
    std::string m_ObjectName; // name of bindable object

    typedef struct _SUserData{CLuaObjectBinder* object;} SUserData;



    static int closure(lua_State* state)
    {
               // получаем метатаблицу для объекта
        luaL_getmetatable(state, "obj"); // костыль !!!!

        CThisData* data = (CThisData* )lua_touserdata(state, lua_upvalueindex(1));
        return data->_this->OnMethod(state,data->MethodName);
    }

public:
    void AddMethod(const std::string& name)
    {
        CThisData dt;
        dt._this = this;
        dt.MethodName = name;
        m_Methods.push_back(dt);
    };

    int OnMethod(lua_State* state, const std::string& methodName)
    {
        std::string str = "Method name called: ";
        str += methodName;
        MessageBox(NULL,str.c_str(),"In C++",MB_OK);

        if(methodName == "GetMessage")
        {
            luaPushParam(state,"returned from dynamic binder!");
            return 1;
        }

        return 0;
    }

    void Bind(lua_State* state, const char* funcName)
    {
        lua_newtable(state);
        int methods = lua_gettop(state);

        luaL_newmetatable(state, m_ObjectName.c_str());
        int metatable = lua_gettop(state);

        lua_pushstring(state, m_ObjectName.c_str());
        lua_pushvalue(state, methods);
        lua_settable(state, LUA_GLOBALSINDEX);

        lua_pushstring(state, "__metatable");
        lua_pushvalue(state, methods);
        lua_settable(state, metatable);

        lua_pushstring(state, "__index");
        lua_pushvalue(state, methods);
        lua_settable(state, metatable);

                // регистрируем методы
        for(std::vector<CThisData>::iterator it = m_Methods.begin();it != m_Methods.end(); ++it)
        {
            lua_pushstring(state, (*it).MethodName.c_str());
            lua_pushlightuserdata(state, (void* )&(*it));
            lua_pushcclosure(state, closure, 1);
            lua_settable(state, methods);
        }

        lua_pop(state, 2);


        // get function pointer
        lua_getfield(state, LUA_GLOBALSINDEX, funcName);
        // call function
        lua_call(state,0,0);
    }

private:
    CLuaObjectBinder(const CLuaObjectBinder& rhs);
    CLuaObjectBinder operator=(const CLuaObjectBinder& rhs);
public:
    CLuaObjectBinder(const char* name)
    {
        m_ObjectName = name;
    };
    ~CLuaObjectBinder()
    {
    };
};


Как это работает:

    CLuaObjectBinder binder("obj");
    binder.AddMethod("GetMessage");
    binder.Bind(luaHandle,"object_test");


Из кода видно, что теперь в функции object_test можно обращаться к объекту obj по имени, и даже вызывать его методы. Но хочется-то передавать экземпляр этого объекта в функцию, т.е.:
function object_test(obj)
    alert(obj.GetMessage());
end


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

Так что вопрос стоит так: как сказать LUA, что параметр, передаваемый в функцию — это объект со своей уже настроенной таблицей методов? Чтобы LUA знал, что есть такие методы у объекта, и брал их из метатаблицы. Я так подозреваю, что теоретически это можно сделать следующим образом: создать новую таблицу, положить туда все методы, кинуть все это в стек, затем кинуть какой-либо объект, который будет привязан к этой таблице. Проблема начинается со слов "какой-либо объект".

Ы?

З.Ы. Сорри за многабукаф.
Re: Вызов методов экземпляра объекта из функции LUA
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 07.02.09 14:18
Оценка:
Всем спасибо, разобрался, тема закрыта.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.