Question

I'm implementing Lua as a script language into a Windows application. Due to the application's structure, printout isn't using streamed io, such as stdout and stderror.

I have managed to override the Lua print to fit into my structure...

lua_register(L,"print", cs_print);

...but how do I override all error an debug printouts without using streams? I need to handle this in a function (similar to print).

Was it helpful?

Solution 2

After a lot of Google I came up with this solution to fetch compiler and runtime error messages as well to redirect the standar Lua print function.

I use C++ Builder but I think it can be useful for anyone looking for the same answer. The script is running in a TScriptLua object instance and to map a Lua state against the correct script instance, I use a std::map list to look up the object pointer.

// list for mapping Lua state with object pointers.
static std::map<lua_State*,TScriptLua*> LuaObjMap;

Next is an inline function to get the object pointer from a Lua state pointer.

extern "C" {

   // Inline to map Lua state pointer to object pointer
   static inline TScriptLua* GetScriptObject(lua_State* L) {
     return LuaObjMap.find(L)->second;
   }

This function will replace the standard Lua print function. The object pointer (f) has a member function in its base class called Out() that will output a char buffer in the associated window control.

   // New Lua print function
   static int cs_print (lua_State *L) {

     // Map Lua state to object
     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); // Get string
         size_t len = lua_rawlen(L,i);         // Get string length

         // Output string.
         f->Out(str,len);
       }
     }

     return 0;
   }

This is my error print routine that will display the compiler/runtime error. As for the Lua print function, the f->Out is used again to print the error message.

   // Error print routine
   static int cs_error(lua_State *L, char *msg) {

     // Map Lua state to object
     TScriptLua* f = GetScriptObject(L);

     // Get error message
     AnsiString m = String(msg) + " " + String(lua_tostring(L, -1));

     // "print" error message
     f->Out(m.c_str(),m.Length());

     // Clenaup Lua stack
     lua_pop(L, 1);

     return 0;
   }

 } // <--- End extern C

Here is the actual load and run member. After the new Lua state has been created it is associated with this object in the mapping list. Then it loads the script from a Rich edit control with luaL_loadbuffer(), checks for compiler errors and runs the compiled script with lua_pcall().

void __fastcall TScriptLua::Compile(void) {

   // Create new Lua state
   lua_State *L = luaL_newstate();

   // Store mapping Lua state --> object
   LuaObjMap.insert( std::pair<lua_State*,TScriptLua*>(L,this) );

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

   // Load and compile script
   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))        // Run loaded Lua script
       cs_error(L, "Runtime error: "); // Print runtime error
   } else {
     cs_error(L, "Compiler error: ");  // Print compiler error
   }

   // Close Lua state
   lua_close(L);

   // Remove Lua --> object mapping
   LuaObjMap.erase( LuaObjMap.find(L) );

 }

This isn't my final solution, but it does the trick so far. I think the best thing is to write stream handling into the TScriptLua object so I can register it directly into Lua.

OTHER TIPS

The only place where Lua writes to stderr is in the panic function that luaL_newstate installs. If you're embedding Lua into your application, make sure you start Lua from a protected call and no panic will ever occur. See http://www.lua.org/source/5.2/lua.c.html#main for ideas.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top