How to replace Lua default error print? - c++

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).

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.

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.

Related

LUA & C++: How to properly use C++ function inside LUA's code

I'm currently learning using LUA in C++ code. And I came to a point where I cannot figure out how to use C++ function in LUA's code.
I want to create simple function in c++ and then use it in LUA.
My approach is as follows (taken from some tutorials):
In main.cpp:
void write(const char* str) {
std::cout<<str<<std::endl;
}
static int l_write(lua_State* L) {
const char* str = lua_tostring(L, 1); // get function argument
write(str); // calling C++ function with this argument...
return 0; // nothing to return!
}
int main(){
lua_State* L = luaL_newstate();
luaL_openlibs(L); // load default Lua libs
if (luaL_loadfile(L, "test.lua")) {
std::cout<<"Error loading script"<<std::endl;
}
lua_pushcfunction(L, l_write);
lua_setglobal(L, "write"); // this is how function will be named in Lua
lua_pcall(L, 0, 0, 0); // run script
}
and in test.lua I've got:
write("Hello, world!")
write("The square root of 2 is "..math.sqrt(2))
x = 42
write("We can use variables too, x = "..x)
The problem appears in the very beginning of this code: I cannot even a load script luaL_loadfile(L, "test.lua") returns value 7 (which is as I checked NIME_AGAIN 7 /* temporarily out of resources */).
Everything else works just fine do provided I don't use my custom c++ functions. I can load values from LUAs file normally, can execute function ect.
I suppose that LUA after reading file already compiles it and then finds out name of function that does not exist, namely "write" and returns error in case of reading this file, is it possible? And if so how to resolve this problem, and how to use this feature correctly?
Mhm, guys. It is strange but I did lua_pop(L, 1) once, run and then delete it and now its working just fine O.o

How to pass variables to lua function in cocos2d-x?

I am trying to call lua functions in cocos2d-x. However when I try to pass some variables to lua functions. My program stopped at lua_call().
My function:
const char* getData::callLuaFunction(const char* luaFileName,const char* functionName){
lua_State* ls = CCLuaEngine::defaultEngine()->getLuaStack()->getLuaState();
std::string filefullpath = CCFileUtils::sharedFileUtils()->fullPathForFilename(luaFileName);
const char* pfilefullpath = filefullpath.c_str();
int isOpen = luaL_dofile(ls, pfilefullpath);
if(isOpen!=0){
CCLOG("Open Lua Error: %i", isOpen);
return NULL;
}
lua_getglobal(ls, functionName);
lua_pushstring(ls, "einverne");
lua_pushnumber(ls, 2);
lua_pushboolean(ls, true);
lua_call(ls, 3, 1);
const char* iResult = lua_tostring(ls, -1);
return iResult;
}
Function in lua file:
function luaLogString(_logStr,_logNum,_logBool)
    print("Lua string from C:",_logStr,_logNum,_logBool)
    return "call lua function OK"
end
Edit:
I have found lua_call is not protected. lua_pcall function is safer. And after I changed to lua_pcall. Errors show that attempt to call global '聽聽聽聽print' (a nil value)
Actually I found the problem.
I delete four space before print function in lua file and everything is OK.
And I suggest newbie to use lua_pcall rather than lua_call. Because if there is an error when calling lua_call , this function will call exit(EXIT_FAILURE) and shutdown host program without giving an error message.
The difference between lua_pcall and lua_call
English version
Chinese version

Calling lua function that returns table

I know the basics of interacting with lua and C, and I am currently trying to perform the following line of lua in c++
Func1():Func2().Table1.value1
I am trying to get the value of "value2" and use it in my C program. The following is the code I wrote to attempt to get this value in C.
int GetNumber()
{
int retn = 0;
g_clientlua.lua_getfield(LUA_REGISTRYINDEX, "Player");
g_clientlua.lua_getfield(-1, "Func2");
g_clientlua.lua_getfield(LUA_GLOBALSINDEX, "Func1");
g_clientlua.lua_call(0, 1);
g_clientlua.lua_call(1, 1);
if (g_clientlua.lua_isnil(-1))
return retn;
g_clientlua.lua_getfield(-1, "Table1");
if (g_clientlua.lua_isnil(-1))
return retn;
g_clientlua.lua_getfield(-1, "value1");
if (g_clientlua.lua_isnil(-1))
return retn;
retn = (int)g_clientlua.lua_tointeger(-1);
}
The clientlua thing is an object that basically just allows me to call a method which calls it's lua_* function equivalent and fills the lua_state pointer parameter with a member variable that is a pointer to the lua state.
Every time I call this, it complains about me causing a lua stack leak. To solve this, I tried adding a lua_pop(3) to the end, but then it just crashes my program without reporting an error, so I assume I am doing something wrong.
Anyone have any words of wisdom for me? Kinda lost here. I doubt the above code is even written properly, how would I write the above lua call in C?
You need to call Func1 before you try to get Func2 as Func2 comes from the table that Func1 returns (and not from the global table).
Then you need to call Func2 and look up Table1 in that returned value, etc.
What "stack leak" complaint are you getting? If you are calling this function from C directly then yes, you need to be sure that anything you put on the lua stack (that isn't for consumption by the caller, etc.) is popped from the lua stack before you return.
The GetNumber function isn't doing exactly the same as the lua snippet you're going for. Specifically GetNumber is getting the value of "Func2" from the registry while your lua snippet is getting the value of "Func2" from the table returned by Func1(). Unless you're certain that registry.Player.Func2 == Func1().Func2 is always true, your C++ version will not have the same behavior.
Let's break down Func1():Func2().Table1.value1 into more explicit steps to help with the C translation:
Get function associated with _G.Func1
Call that function and get a table back
Get function associated with "Func2" from the returned table in step 2
Call that function and pass as argument the table from step 2. Get another table back as result
I found it helpful to track what the stack contains as a side-comment as the operations are performed:
int GetNumber()
{
// Func1()
gclientlua.lua_getfield(LUA_GLOBALSINDEX, "Func1"); // Func1
g_clientlua.lua_call(0, 1); // {}
// Func2( {} )
g_clientlua.lua_getfield(-1, "Func2"); // {}, Func2
g_clientlua.lua_insert(-2); // Func2, {}
g_clientlua.lua_call(1, 1); // {}
if( g_clientlua.lua_type(-1) != LUA_TTABLE )
{
g_clientlua.lua_pop(1);
return 0;
}
// {}.Table1
g_clientlua.lua_getfield(-1, "Table1"); // {}, {}(Table1)
if( g_clientlua.lua_type(-1) != LUA_TTABLE )
{
g_clientlua.lua_pop(2);
return 0;
}
// tonumber( Table1.value1 )
g_clientlua.lua_getfield(-1, "value1"); // {}, {}(Table1), value1
int retn = g_clientlua.lua_tointeger(-1);
g_clientlua.lua_pop(3);
return retn;
}
Notice that GetNumber pops off all the arguments it places on the stack before returning. This ensures that GetNumber leaves the lua stack the way it was found. This can probably be automated with RAII if you're using C++.

luabind: Can't call basic lua functions like print, tostring

A very basic question i guess:
The C++ code, calling lua looks like this:
lua_State* m_L;
m_L = lua_open();
luabind::open(m_L);
luaL_dofile(m_L, "test.lua");
try {
luabind::call_function<void>(m_L, "main");
} catch (luabind::error& e) {
std::string error = lua_tostring(e.state(), -1);
std::cout << error << std::endl;
}
lua_close(m_L);
now test.lua has the following contents:
function main()
print "1"
end
Upon execution I receive the error:
test.lua:2: attempt to call global 'print' (a nil value)
What is the problem? It has something to do with environments? I thought functions like print are defined in the global environment. Why is it not found then?
Thank you very much.
As you figured it out, you have to call luaopen_base to get print and other base functions. Then you need to call luaopen_string, luaopen_math, to get the basic modules and functions in. Instead of writing it all out manually, can load all Lua base function at once with luaL_openlibs:
lua_State* m_L = luaL_newstate();
luaL_openlibs(m_L);

Redirecting/redefining print() for embedded Lua

I have embedded Lua in my C++ application. I want to redirect print statements (or maybe simply redefine the print function?), so that I can display the evaluated expression somewhere else.
What is the best way to do this: redirect or redefining the print() function?
Any snippets/pointers to snippets that show how to do this would be much appreciated.
You can redefine the print statement in C:
static int l_my_print(lua_State* L) {
int nargs = lua_gettop(L);
for (int i=1; i <= nargs; i++) {
if (lua_isstring(L, i)) {
/* Pop the next arg using lua_tostring(L, i) and do your print */
}
else {
/* Do something with non-strings if you like */
}
}
return 0;
}
Then register it in the global table:
static const struct luaL_Reg printlib [] = {
{"print", l_my_print},
{NULL, NULL} /* end of array */
};
extern int luaopen_luamylib(lua_State *L)
{
lua_getglobal(L, "_G");
// luaL_register(L, NULL, printlib); // for Lua versions < 5.2
luaL_setfuncs(L, printlib, 0); // for Lua versions 5.2 or greater
lua_pop(L, 1);
}
Since you are using C++ you'll need to include your file using 'extern "C"'
You can simply redefine print from a Lua script.
local oldprint = print
print = function(...)
oldprint("In ur print!");
oldprint(...);
end
See luaB_print in lbaselib.c. The comment there reads:
/* If you need, you can define your own `print' function, following this
model but changing `fputs' to put the strings at a proper place (a
console window or a log file, for instance). */
You can just edit that function or define a new one. This has the advantage of being simple and portable, but it won't handle io.write (which you may or may not care about).
Redirecting IO isn't going to be platform specific (such as SetStdHandle in Windows), but will take care of print and io.write without redefining either.
Write your own C or Lua function and redefine print.
You can just redefine the following macros:
lua_writestring
lua_writeline
lua_writestringerror
to whatever you like.
I'm not sure about the lua version where this was introduced - but it works in 5.3 for me.
Check your lauxlib.h or luaconf.h.