vinniefalco / LuaBridge

A lightweight, dependency-free library for binding Lua to C++
1.62k stars 346 forks source link

LUA C-style function hooks vs. delegates #64

Open vsariola opened 10 years ago

vsariola commented 10 years ago

Hi,

First of all, thank you all for great work! I am using LUABridge for almost everything and binding C++ to LUA is now easy and straightforward. Except for the debug hooks. First a little background: I am using debug hooks to kill a script running in another thread. Since C doesn't have exceptions, pure C and LUA implementation uses setjmp.h in a following way:

jmp_buf place;
...
lua_sethook(L, hook, LUA_MASKLINE, 1);
if (setjmp(place) == 0)
     luaL_dostring(L,...)
...
void hook(lua_State* L, lua_Debug *ar)
{
   if (script_should_be_killed)
        longjmp(place, 1);      
}

With C++, we replace setjmp with the usual exception handling, e.g.

lua_sethook(L, hook, LUA_MASKLINE, 1);
try {
  luaL_dostring(L,...)
}  catch (...) {
}
...
void hook(lua_State* L, lua_Debug *ar)
{
   if (script_should_be_killed)
        throw std::exception();
}

Now, I have encapsulated the lua_State into a C++ class ScriptEngine, which provides thread safe calls to run and kill scripts, and there's a 1-1 relationship between ScriptEngine and lua_State.

void ScriptEngine::hook(lua_State* L, lua_Debug *ar)

But lua_sethook requires the hook to be a static function and I only get a pointer to lua_State, not ScriptEngine. So, in the hook, I need a way to find the associated ScriptEngine based on lua_State*. I am currently using a static hash map to maintain links back from lua_States to ScriptEngines, but that doesn't feel right. Long story short, I would need either:

1) LUABridge providing it's own sethook, that can hook to member functions. This would further C++:ize access to LUA.

2) A reliable way to associate my own data with lua_State i.e. putting a pointer to ScriptEngine in the lua_State. I was thinking of putting it to the stack or making it global, but I felt paranoid about the risk of the data getting overwritten by the script. My hypothetical worry is that a script could accidentally break it's emergency killing mechanism, and I really need to be able to kill my scripts, always.

merlinblack commented 10 years ago

What I do to associate my script management class with a Lua state is store a light userdata pointer to the class instance in the Lua registry. As a key I use the address of a file scope static variable. Have a look here for an example, lines 45 - 65, and 207-208. In my 'callback' function, I retrieve the pointer to the instance.

https://github.com/merlinblack/Game-Engine-Testbed/blob/master/src/scriptingsystem.cpp

In my case I only have one instance of my script management class, however as you have a 1 - 1 relationship with lua_State, it should work for you too.

Hope that helps!