c++event-handlinglua

Storing reference to lua function in C


I have a basic event handler implemented in C++. I also have an embedded Lua interpreter in my application that I need to interact with the Event Manager. The ultimate goal is to be able to have one event handler that will execute both c++ and Lua functions when an event is fired.

My problem is that I can't come up with a simple way to store references to the lua functions in my C++ code. I know how to execute Lua functions from c (using lua_getglobal and lua_pcall), but I would prefer to store a reference to the function itself, so that I can pass a Lua function directly to registerListener

Note It is acceptable to assume that userdata will be NULL for all Lua Listeners.

Here's my code:

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;
}

Solution

  • I would suggest to store your functions into the registry and use the reference mechanism provided by the functions luaL_ref and luaL_unref.

    These functions use an C int value to access the values. It is easy to store such an integer value in a C++ class member for example.