Как мне связать функции lua с функциями C ++? - PullRequest
2 голосов
/ 29 апреля 2011

У меня есть класс с именем Entity, который имеет много функций, таких как onPickup, onDrop, onUse и т. Д. Я хочу написать скрипт, который определяет все эти функции и делает их вызываемыми из функций C ++. Таким образом, функции, определенные в C ++, будут просто вызывать соответствующие им функции Lua, которые имеют некоторую функциональность.

Но вот проблема, я хочу, чтобы каждый сценарий, который я пишу, для каждого объекта в программе работал в своей области видимости.

Я использую LuaBind, и у меня нет опыта работы с Lua, поэтому я немного растерялся.

Ответы [ 3 ]

3 голосов
/ 29 апреля 2011

Я не использую lua bind, но это может помочь. Идея состоит в том, чтобы зарегистрировать функции lua в вашем классе C ++ и сохранить ссылку на функцию lua в вашем классе C ++.

Чтобы определить функцию lua, которая вызывается из C / C ++, я использую luaL_ref для хранения ссылки на функцию обратного вызова в моем объекте C ++.

// a little helper function
template <typename T>
T *Lua_getUserData(lua_State *L) {
    assert(lua_isuserdata(L, 1) == 1);
    T **v = (T **) lua_touserdata(L, 1);
    assert(v != NULL);
    return *v;
}

int lua_FormRegisterMethods(lua_State *L) {
    Entity *f = Lua_getUserData<Entity>(L);
    assert(lua_istable(L, 2) == 1); // check the next parameter is a table
    lua_pushvalue(L,2); // dup the table
    f->LuaTable = luaL_ref(L, LUA_REGISTRYINDEX); // keep a reference to the table
    lua_getmetatable(L, 2); // get the metatable
    lua_pushstring(L, "OnClick"); 
    lua_rawget(L, -2); // get the OnClick Lua Function
    f->LuaMethod = luaL_ref(L, LUA_REGISTRYINDEX); // save a reference to it
    return 0;
}

и тогда вы можете получить метод lua в вашем событии C ++

lua_rawgeti( LuaInstance->L, LUA_REGISTRYINDEX, LuaMethod );
assert(lua_isfunction(LuaInstance->L, -1) == 1);

теперь вы можете вызывать эту функцию, установив self в таблицу, которую вы сохранили ранее. НТН

1 голос
/ 29 апреля 2011

Вы можете вызвать функцию Lua с помощью, например,

int callLuaFunction(lua_State* lua_state) {
    return luabind::call_function<int>(lua_state, "myluafunction", param1);
}

, если функция Lua возвращает int и принимает 1 параметр.

Я почти уверен, что вы можете сделать столько lua_State, сколько захотите.Просто передайте правильный для сущности в call_function.

0 голосов
/ 29 апреля 2011

Чтобы полностью реализовать это так, как вы, вероятно, захотите, потребуется немного покопаться в некоторых из более эзотерических битов Lua.Хотя это того стоит.Я покажу очень урезанную версию того, как я справился с этим.Имейте в виду, что здесь есть много маленьких кусочков, работающих вместе - в основном, сохранение и вызов сохраненных функций и использование объектов c ++ в качестве пользовательских данных Lua.

Сначала нам нужен класс c ++, который будет хранить обработчики событий (функции lua).) как ссылки lua, которые являются простыми целыми числами.Я использую массив здесь, но вы можете использовать все, что имеет смысл.Главное, что здесь происходит, это то, что вы хотите иметь возможность вызывать функцию lua, на которую ссылается ссылка int.

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include <string>
#include <iostream>
#include <assert.h>
using namespace std;

enum enum_event_types {
    ON_USE, ON_DROP, ON_WHATEVER, EVENT_COUNT
};

class Entity {
  private:  
    int events[EVENT_COUNT];
  public: 
    lua_State* lua;
    void setEventHandler(int event, int ref) {
        assert(event < EVENT_COUNT);
        events[event] = ref;
    }
    void callEventHandler(int event) {
        int error;
        assert(event < EVENT_COUNT);
            // to call the function we need to get it from the registry index
        lua_rawgeti(lua, LUA_REGISTRYINDEX, events[event]);
        error = lua_pcall(lua, 0, 0, 0); // use protected call for errors
        if (error) {
            printf("error: %s", lua_tostring(lua, -1));
            lua_pop(lua, 1); 
        }
    }
};

Теперь вы хотите представить свой класс сущностей Луа.Если вы не знакомы с тем, как это делается, есть хорошая статья здесь .По сути дела, мы устанавливаем пользовательские данные, возвращаемые из Entity.new (), указателем на указатель.Это так, что Lua не собирает мусор для вашего объекта.Затем создайте мета-таблицу для «Entity», которая будет содержать все методы, доступные для Lua.

int L_newEntity(lua_State* L) {
    Entity **e = (Entity **)lua_newuserdata(L, sizeof(Entity *));
    *e = new Entity(); 
    (*e)->lua = L;
    lua_getglobal(L, "Entity");
    lua_setmetatable(L, -2); 
    return 1;
}

int L_setOnUse(lua_State* L) {
    Entity** e = (Entity**) lua_touserdata(L, 1);
    lua_pushvalue(L, 2);
    int ref = luaL_ref(L, LUA_REGISTRYINDEX);
    (*e)->setEventHandler(ON_USE, ref);
    return 0;
}

// this will be exposed to Lua as a table called Entity
static const luaL_Reg L_entityMethods[] = {
    {"new", L_newEntity},{"setOnUse", L_setOnUse},{NULL, NULL}
};

Теперь настройте состояние Lua, создайте таблицу Entity и создайте тест.Тест создаст Entity и установит в его обработчике события функцию Lua, переданную ему.Наконец, проверьте, что функция Lua вызывается объектом c ++.

int main() {
    Entity** e;
    int error;
    lua_State* L=lua_open();
    luaL_openlibs(L);
    luaL_register(L, "Entity", L_entityMethods); 
    lua_pushvalue(L,-1);
    lua_setfield(L, -2, "__index"); 
    lua_pop(L, 1);

    luaL_loadstring(L, 
    "e = Entity.new(); "
    "e:setOnUse(function()"
    "   print('Some of them want to use you')"
    "end);");
    error = lua_pcall(L, 0, 0, 0);
    if (error) {
        printf("error: %s", lua_tostring(L, -1));
        lua_pop(L, 1); /* errors must be popped from stack */
    }   
    lua_getglobal(L, "e");
    if (lua_isuserdata(L, 1)) {
        e = (Entity**) lua_touserdata(L, 1);
        (*e)->callEventHandler(ON_USE);
    }
    return 0;
}

Это далеко не завершено.Прежде всего, если вам когда-нибудь понадобится установить обработчик событий дважды, вам нужно будет использовать luaL_unref, чтобы сначала очистить старую ссылку.Во-вторых, вы, вероятно, захотите передать некоторые данные о событии, которое произошло, обработчику события.Текущий обработчик событий не принимает никаких данных, поэтому дает пользователю API очень мало для продолжения.Вероятно, он должен хотя бы передать ссылку на объект, который вызывает событие.Это может быть использовано для создания очень мощного и удобного Apis в Lua.Удачи!

...