之前介绍了在 C/C++ 中如何调用 Lua 函数, 但是那种方法并不灵活, 它依赖于 Lua 函数的名字. 在不考虑获取函数返回值的情况下, 使用一种事件驱动的模型来与 Lua 代码交互会更便于维护和扩展.
如果要使用事件驱动模型的话, 那么它的过程应该是: 由 C/C++ 代码触发事件, 然后发送给 Lua 代码, 由 Lua 执行相应的功能. 这里就需要一个”媒介”, 通过它来统一事件触发函数和事件处理函数(类似 MFC 中的控件 ID).
那来看看怎么设计这个事件驱动模型. 首先, 提供一个 LuaGlue 函数来设定在 Lua 脚本中的事件处理程序.
LuaGlue _RegisterEvent(lua_State* L) { g_strEventHandle = g_lua.GetStringArgument(1, ""); return 0; }
那么, 再来设计发送事件函数.
void FireEvent(int nId, const char* args) { if (g_strEventHandle != "") { char buf[256]; if (args) { sprintf_s(buf, 255, "%s(%d, %s)", g_strEventHandle.c_str(), nId, args); } else { sprintf_s(buf, 255, "%s(%d)", g_strEventHandle.c_str(), nId); } g_lua.RunString(buf); } }
第一个参数是事件ID, 这个很好理解, 那第二个参数是什么?
因为 Lua 是支持变参系统的, 所以我们可以为每个事件设定不同的参数, 而第二个参数是用来表示那些可变参数的.
接下来, 看看 Lua 事件处理函数的模板:
local EVENT_SAMPLE = 1000 RegisterEvent("EventHandle") function EventHandle(nId, ...) if nId == EVENT_SAMPLE then print("Sample Event!") for i, v in ipairs{...} do print(v) end end end
现在, 我们在 C/C++ 的主程序中发出事件, 看看会输出什么:
#define EVENT_SAMPLE 1000 cLua g_lua; g_lua.RunScript("C:\\Users\\Administrator\\Desktop\\test.lua"); FireEvent(EVENT_SAMPLE, "\"你好\", 10");
看! 一个基于 C/C++ 和 Lua 的事件驱动模型建立起来了. 这个系统需要注意的地方在于, Lua 代码和 C/C++ 代码需要对事件 ID 和传递的参数的含义保持一致.
下面是完整的 C/C++ 端代码:
std::string g_strEventHandle = ""; cLua g_lua; LuaGlue _RegisterEvent(lua_State* L) { g_strEventHandle = g_lua.GetStringArgument(1, ""); return 0; } void FireEvent(int nId, const char* args) { if (g_strEventHandle != "") { char buf[256]; if (args) { sprintf_s(buf, 255, "%s(%d, %s)", g_strEventHandle.c_str(), nId, args); } else { sprintf_s(buf, 255, "%s(%d)", g_strEventHandle.c_str(), nId); } g_lua.RunString(buf); } } #define EVENT_SAMPLE 1000 int _tmain(int argc, _TCHAR* argv[]) { g_lua.AddFunction("RegisterEvent", _RegisterEvent); g_lua.RunScript("C:\\Users\\Administrator\\Desktop\\test.lua"); FireEvent(EVENT_SAMPLE, "\"你好\", 10"); system("PAUSE"); return 0; }
☠