Хранение ссылки на функцию lua в C

У меня есть основной обработчик событий, реализованный в C ++. У меня также есть встроенный интерпретатор Lua в моем приложении, который мне нужен для взаимодействия с менеджером событий. Конечная цель — иметь возможность иметь один обработчик событий, который будет выполнять функции как c ++, так и Lua при возникновении события.

Моя проблема в том, что я не могу придумать простой способ хранения ссылок на функции lua в моем коде C ++. Я знаю, как выполнять функции Lua из c (используя lua_getglobal а также lua_pcall), но я бы предпочел сохранить ссылку на саму функцию, чтобы я мог передать функцию Lua непосредственно registerListener

Заметка Можно предположить, что данные пользователя будут NULL для всех слушателей Lua.

Вот мой код:

EventManager.h

#include <string>
#include <map>
#include <vector>

using namespace std;

typedef void (*fptr)(const void* userdata, va_list args);
typedef pair<fptr, void*> Listener;
typedef map<string, vector<Listener> > CallbackMap;

class EventManager {
private:
friend ostream& operator<<(ostream& out, const EventManager& r);

CallbackMap callbacks;
static EventManager* emInstance;
EventManager() {
callbacks = CallbackMap();
}
~EventManager() {
}
public:
static EventManager* Instance();
bool RegisterEvent(string const& name);
void RegisterListener(string const &event_name, fptr callback,
void* userdata);
bool FireEvent(string name, ...);
};

inline ostream& operator<<(ostream& out, const EventManager& em) {
return out << "EventManager: " << em.callbacks.size() << " registered event"<< (em.callbacks.size() == 1 ? "" : "s");
}

EventManager.cpp

#include <cstdarg>
#include <iostream>
#include <string>

#include "EventManager.h"
using namespace std;

EventManager* EventManager::emInstance = NULL;

EventManager* EventManager::Instance() {
if (!emInstance) {
emInstance = new EventManager;
}

return emInstance;
}

bool EventManager::RegisterEvent(string const& name) {
if (!callbacks.count(name)) {
callbacks[name] = vector<Listener>();
return true;
}

return false;
}

void EventManager::RegisterListener(string const &event_name, fptr callback,
void* userdata) {
RegisterEvent(event_name);
callbacks[event_name].push_back(Listener(callback, userdata));
}

bool EventManager::FireEvent(string name, ...) {
map<string, vector<Listener> >::iterator event_callbacks =
callbacks.find(name);
if (event_callbacks == callbacks.end()) {
return false;
}

for (vector<Listener>::iterator cb =
event_callbacks->second.begin();
cb != event_callbacks->second.end(); ++cb) {
va_list args;
va_start(args, NULL);
(*cb->first)(cb->second, args);
va_end(args);
}

return true;
}

luaw_eventmanager.h

#pragma once
#ifndef LUAW_EVENT_H
#define LUAW_EVENT_H

#include "EventManager.h"
extern "C" {

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

void luaw_eventmanager_push(lua_State* L, EventManager* em);
int luaopen_weventmanager(lua_State* L);

}
#endif

luaw_eventmanager.cpp

#include <assert.h>
#include <stdio.h>

#include <sstream>
#include <iostream>

#include "luaw_eventmanager.h"
using namespace std;

static int
luaw_eventmanager_registerevent(lua_State* L)
{
int nargs = lua_gettop(L);
if (nargs != 2) {
return 0;
}

stringstream ss;
ss << luaL_checkstring(L, 2);

EventManager::Instance()->RegisterEvent(ss.str());
return 1;
}

static int
luaw_eventmanager_registerlistener(lua_State* L)
{
return 1;
}

static int
luaw_eventmanager_fireevent(lua_State* L)
{
return 1;
}

static int
luaw_eventmanager_tostring(lua_State* L)
{
stringstream ss;
ss << *EventManager::Instance();
lua_pushstring(L, &ss.str()[0]);
return 1;
}

static const struct luaL_Reg luaw_eventmanager_m [] = {
{"registerEvent", luaw_eventmanager_registerevent},
{"registerListener", luaw_eventmanager_registerlistener},
{"fireEvent", luaw_eventmanager_fireevent},
{"__tostring", luaw_eventmanager_tostring},
{NULL, NULL}
};

void
luaw_eventmanager_push(lua_State* L, EventManager* em)
{
EventManager** emUserdata = (EventManager**)lua_newuserdata(L, sizeof(EventManager*));
*emUserdata = em;
luaL_getmetatable(L, "WEAVE.mEventManager");
lua_setmetatable(L, -2);
}

int
luaopen_weventmanager(lua_State* L)
{
luaL_newmetatable(L, "WEAVE.mEventManager");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_register(L, NULL, luaw_eventmanager_m);
assert(!lua_isnil(L, -1));
return 1;
}

7

Решение

Я бы предложил сохранить ваши функции в реестр и использовать механизм ссылок, предоставляемый функциями luaL_ref а также luaL_unref,

Эти функции используют C int значение для доступа к значениям. Например, такое целочисленное значение легко хранить в члене класса C ++.

6

Другие решения

Все объекты, принадлежащие Lua, собираются мусором. Это включает в себя функции. Поэтому, даже если бы вы могли получить ссылку на функцию Lua, Lua все равно своя это и, следовательно, это будет предметом GC всякий раз, когда Lua обнаружит, что на него больше не ссылаются.

Внешний код не может иметь ссылку на Lua. Но внешний код может хранить та ссылка в месте, куда код Lua не может достичь (и, следовательно, не может сломаться): реестр Lua.

Реестр Lua таблица Lua (которая находится в псевдоиндексе стека LUA_REGISTRYINDEXтак что он доступен из стека), к которому Lua-код не может (напрямую) получить доступ. Таким образом, это безопасное место для хранения того, что вам нужно. Поскольку это таблица Lua, вы можете манипулировать ею, как и любой другой таблицей Lua (добавление значений, ключей и т. Д.).

Тем не менее, реестр является глобальным, и если вы используете другие модули C, вполне возможно, что они могут начать давить друг на друга. Поэтому рекомендуется выбрать определенный раздел реестра для каждого из ваших модулей и создать таблицу в этом разделе реестра.

Первый шаг: при инициализации кода интерфейса C, создайте таблицу и вставьте ее в известный ключ в таблице реестра. Просто пустой стол.

Когда код Lua передает вам функцию Lua для использования в качестве обратного вызова, загрузите эту таблицу из специальной клавиши и вставьте туда функцию Lua. Конечно, для этого вам нужно дать каждой зарегистрированной функции уникальный ключ (который вы храните как функцию Lua void* данные), который вы можете позже использовать для извлечения этой функции.

У Lua есть простой механизм для этого: luaL_ref. Эта функция зарегистрирует объект на вершине стека с таблицей, которую ему дали. Этот процесс регистрации гарантированно возвращает уникальные целочисленные ключи для каждого зарегистрированного объекта (если вы не вручную изменить таблицу за спиной системы). luaL_unref выпускает ссылку, алло

Поскольку ссылки являются целочисленными ключами, вы мог просто сделать бросок из int в void* и это будут данные. Я бы, вероятно, использовал явный объект (mallocв int), но вы можете хранить его так, как вам нравится.

Шаг второй: когда функция Lua зарегистрирована, используйте luaL_ref чтобы добавить его в таблицу реестра, созданную на шаге 1. Сохраните ключ, возвращенный этой функцией, в void* параметр для зарегистрированной функции.

Шаг третий: когда нужно вызвать эту функцию, используйте целочисленную клавишу, которую вы сохранили в void* Параметр для доступа к таблице реестра, созданной на шаге 1. Это даст вам функцию, которую вы затем сможете вызывать с помощью обычных методов Lua.

Шаг четвертый: когда вы отменяете регистрацию функции Lua, используйте luaL_unref чтобы освободить функцию (таким образом вы избегаете утечки памяти Луа). если ты mallocпамять для хранения целочисленного ключа, free это здесь.

16

@ Николас Болас предоставил хорошие инструкции, но они слишком расплывчаты для новичков (включая меня).
Методом проб и ошибок я придумал рабочий пример:

Место хранения

lua_newtable(L);  // create table for functions
int tab_idx = luaL_ref(L,LUA_REGISTRYINDEX); // store said table in pseudo-registry
lua_rawgeti(L,LUA_REGISTRYINDEX,tab_idx); // retrieve table for functions
lua_getglobal(L, "f"); // retrieve function named "f" to store
int t = luaL_ref(L,-2); // store a function in the function table
// table is two places up the current stack counter
lua_pop(L,1); // we are done with the function table, so pop it

поиск

lua_rawgeti(L,LUA_REGISTRYINDEX,tab_idx); // retrieve function table
lua_rawgeti(L,-1,t); // retreive function

//use function
//don't forget to pop returned value and function table from the stack
lua_pop(L,2);
3
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector