07. 事件驱动模型

Par @Martin dans le
Tags :

之前介绍了在 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;
}