如何替换Lua的默认错误打印?

我正在将 Lua 作为脚本语言实现到 Windows 应用程序中。由于应用程序的结构,输出并没有使用流式 IO,例如 stdoutstderror

我已经成功地覆盖了 Lua print,使其适应我的结构...

lua_register(L,"print", cs_print);

...但是如何在不使用流的情况下覆盖所有错误和调试输出?我需要在一个函数中处理这个问题(类似于 print)。

点赞
用户107090
用户107090

唯一一个 Lua 写入 stderr 的地方是由 luaL_newstate 安装的 panic 函数中。如果您将 Lua 嵌入您的应用程序中,请确保您从受保护的调用中启动 Lua,将不会发生任何惊恐的情况。有关详细信息,请查看http://www.lua.org/source/5.2/lua.c.html#main

2013-12-08 15:01:22
用户365929
用户365929

经过大量谷歌搜索,我想出了此解决方案,以获取编译器和运行时错误消息,并将标准 Lua 打印函数重定向。

我使用的是 C++ Builder,但我认为对于寻找相同答案的任何人都可能很有用。该脚本在 TScriptLua 对象实例中运行,为了将 Lua 状态映射到正确的脚本实例,我使用 std:: map 列表来查找对象指针。

// 用于映射 Lua 状态与对象指针的列表。
static std::map<lua_State*,TScriptLua*> LuaObjMap;

接下来是一个内联函数,用于从 Lua 状态指针获取对象指针。

extern "C" {

   // 内联函数,用于将 Lua 状态指针映射到对象指针
   static inline TScriptLua* GetScriptObject(lua_State* L) {
     return LuaObjMap.find(L)->second;
   }

此函数将替换标准 Lua 打印函数。对象指针(f)在其基类中具有一个名为 Out()的成员函数,该函数将输出与关联窗口控件中的 char 缓冲区。

   // 新的 Lua 打印函数
   static int cs_print (lua_State *L) {

     // 将 Lua 状态映射到对象
     TScriptLua* f = GetScriptObject(L);

     if (f) {
       int count = lua_gettop(L);
       for (int i=1; i <= count; ++i) {
         const char *str = lua_tostring(L, i); // 获取字符串
         size_t len = lua_rawlen(L,i);         // 获取字符串长度

         // 输出字符串
         f->Out(str,len);
       }
     }

     return 0;
   }

这是我的错误打印常规,它将显示编译器/运行时错误。就 Lua 打印函数而言,再次使用 f->Out 来打印错误消息。

   // 错误打印常规
   static int cs_error(lua_State *L, char *msg) {

     // 将 Lua 状态映射到对象
     TScriptLua* f = GetScriptObject(L);

     // 获取错误消息
     AnsiString m = String(msg) + " " + String(lua_tostring(L, -1));

     // 打印错误消息
     f->Out(m.c_str(),m.Length());

     // 清理 Lua 栈
     lua_pop(L, 1);

     return 0;
   }
 } // <--- 结束 extern C

这是实际的加载和运行成员。创建新的 Lua 状态后,它与此对象关联到映射列表中。然后,它使用 luaL_loadbuffer() 从富文本编辑控件加载脚本,检查编译器错误,并使用 Lua_pcall() 运行编译后的脚本。

void __fastcall TScriptLua::Compile(void) {

   // 创建新的 Lua 状态
   lua_State *L = luaL_newstate();

   // 存储 Lua 状态-->对象映射
   LuaObjMap.insert( std::pair<lua_State*,TScriptLua*>(L,this) );

   // 覆盖 Lua Print
   lua_register(L,"print",  cs_print);

   // 加载和编译脚本
   AnsiString script(Frame->Script_RichEdit->Text);
   if (luaL_loadbuffer(L,script.c_str(),script.Length(),AnsiString(Name).c_str()) == 0) {
     if (lua_pcall(L, 0, 0, 0))        // 运行已加载的 Lua 脚本
       cs_error(L, "Runtime error: "); // 打印运行时错误
   } else {
     cs_error(L, "Compiler error: ");  // 打印编译器错误
   }

   // 关闭 Lua 状态
   lua_close(L);

   // 删除 Lua --> 对象映射
   LuaObjMap.erase( LuaObjMap.find(L) );

 }

这不是我最终的解决方案,但到目前为止它可以胜任。我认为最好的方法是将流处理写入 TScriptLua 对象中,以便可以将其直接注册到 Lua 中。

2013-12-08 23:19:33