Чтобы полностью реализовать это так, как вы, вероятно, захотите, потребуется немного покопаться в некоторых из более эзотерических битов 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.Удачи!