How to properly read string and table arguments with Lua c API? - c++

I spent few hours trying to make it work without success. I have a C++ program with function exposed to Lua script. Lua script uses it like this:
setData('type', {a=1, b=2, c=3})
So first argument is string and second is table.
My current and best version of C++ setData function looks like this:
string sType;
map<string, uint8_t> mOptions;
int nArgs = lua_gettop(i_pState);
if (nArgs == 2)
{
if (lua_isstring(i_pState, 1) != 0)
sType = lua_tostring(i_pState, 1);
if (lua_istable(i_pState, 2) != 0)
{
lua_pushnil(i_pState);
while(lua_next(i_pState, 2))
{
uint8_t nValue = uint8_t(lua_tonumber(i_pState, -1));
string sKey = lua_tostring(i_pState, -2);
mOptions[sKey] = nValue;
lua_pop(i_pState, 1);
}
lua_pop(i_pState, 1);
}
}
return 0;
It works fine but causes "program.exe has triggered a breakpoint" error afterwards at the end when lua_close() function called.
What is wrong with this code?
UPD1. I'm not getting error without handling second argument.
UPD2. My code behaves very strangely. I think it could be issue of mixing extern "C" and C++ code.
UPD3. Turns out this is heap corruption. I can't understand where is cause. Probably this is not related to lua at all.

Finally! Recompilation of Lua code as C++ code didn't work (positive thing is I don't have to worry about extern "C" anymore). But I found two redundant lua_pop() calls. I removed them and now it works fine.
I didn't expect Lua to be so dangerous!

Related

Global variables missing after using require in lua

The problem is, a "require" keyword clears all global variables defined in the same file.
I am working on a project in c++. I use lua 5.3. The lua scripting style is:
1 all lua codes contains 1 common library file, and several small files each acting as a function.
2 when program start, the lua common library is compiled and executed, leave its global variables in the lua state. Then the small files are compiled not executed.
3 when program come to some point, relevant small lua file will be called(just as I say, every small file is a function). The global functions and variables defined in the common library will be used in the small lua file. That's why I compile and execute the common library when program start.
4 I'm very sure I only use 1 lua_State.
It worked fine, until I decide to split common library to multiple files, And use "require" in the main file. When program start, the main file will be compiled and executed.
Not a single code changed, just add come "require". Strange thing happened: All global variables and global functions are missing. When global function is called in small lua file, a "called a nil value" error raises.
I then make a experiment, I delete all "require" lines in the main library file, and it turns out that the global functions defined in the main library worked very well. I then require a empty file "simple.lua", the global functions defined in the main library was missing, cannot be called from the small lua file.
How to explain this? How can a "require" keyword clears all global variables defined in the same file?
in lua library:
require("simple")
function foo()
return 1+1
end
in lua small file
return foo()
result: attempt to call a nil value (global 'foo')
after delete "require", lua library:
function foo()
return 1+1
end
result: no error happens, 2 is returned
the c++ side code:
lua common library compile and excute:
int code = luaL_loadbuffer(L, s, strlen(s), "commonlib");
if(code != 0) {
std::string error = lua_tostring(L, -1);
lua_pop(L, 1);
return error;
}
code = lua_pcall(L, 0, 0, 0);
if(code > 0)
{
std::string ret = lua_tostring(L, -1);
lua_pop(L, 1);
return ret;
}
else
return ""; //empty string means no error
lua small function file compile:
int code = luaL_loadbuffer(L, s, strlen(s), "RULE1");
if(code != 0) {
std::string error = lua_tostring(L, -1);
lua_pop(L, 1);
return error;
}
else
{
return ""; //return empty means no error
}
The L in both code is the same lua state (or it won't work when "require" is removed). After compile, the small file function at the stack top is saved in global register with a reference variable "ref".
When call lua small file, the ref is pushed in the stack, a lua_pcall is used, nothing special.
Resolved the problem by myself.
Because of a duplicated copy/paste, the lua vm is created the second time afterwards. It's rather confusing because if there is no "require", everything is alright. It is a total coincidence.
All I learnt from this is: always double check the copy/paste code.

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

Lua checkudata type C++

I've been to the end of Google and back trying to solve this problem.
I have a few userdata objects that I push from C++ to Lua.
I have a function that should get the X value of either a 2D or 3D object.
When I try to get the userdata object, taking into consideration that it could be either a 2D element or 3D object, I need to be able to get the X for whichever the user chooses.
Here is what I tried:
int getX(lua_State* L)
{
Object3D* a = static_cast<Object3D*>(luaL_checkudata(L, 1, "Object3D"));
if (!a)
{
Object2D* b = static_cast<Object2D*>(luaL_checkudata(L, 1, "Object2D"));
if (b)
{
lua_pushnumber(L, b:getX());
}
else
{
lua_pushnil(L);
}
}
else
{
lua_pushnumber(L, a:getX());
}
return 1;
}
Unfortunately if the userdata type is not Object3D, it fails and exits on an lua error without continuing to try Object2D.
Therefore, it will only work in the above code if the object being passed is of type Object3D.
luaL_testudata
void *luaL_testudata (lua_State *L, int arg, const char *tname);
This function works like luaL_checkudata, except that, when the test fails, it returns NULL instead of raising an error.
The lua(L)_check* functions throw Lua errors on failure, the lua(L)_to* functions return NULL. For whatever reason, this one deviates from the naming convention and is named lua(L)_test* instead, which makes it a bit harder to find.
Your code is incomplete and doesn't compile as-is so I can't be bothered to check, but if I'm not mistaken, just replacing luaL_checkudata with luaL_testudata should make it work as intended.
Solved by using rawequal to see which class the registry matches.

Recursive function causing errors on return (c++)

I'm getting errors in my program between two (theoretically) consecutive lines of code, and have no idea what could cause this.
My whole code is huge, so here's the basics;
int playRoom(std::string currentRoom = "Storage_room", std::string entryDoor = "NULL"){
log("Starting playRoom()");
// code to play the level
// includes setting up box2d world
// and playing the level
if(playerWantsRestart){
log("Restart level");
return playRoom(savedData.roomName, savedData.entryDoor);
}
log("Leaving playRoom()");
return 0;
}
int main( int argc, char* args[] ){
// Set up SDL etc..
playRoom();
log("Back in main()");
// Close SDL
return 0;
}
If I never use the restart option, everything is fine.
If I do use it, the program exits with status 3 and the log file reads:
Starting playRoom()
Restart level
Starting playRoom()
Leaving playRoom()
So the error appears to be in "return 0;"?? I don't think status 3 is an overflow, and it's only recursed (?) once anyway, so...
I'm using Codeblocks 12.11, compiling with GNU GCC. Any help or ideas would be great!
int playRoom(std::string currentRoom = "Storage_room", std::string entryDoor = "NULL")
Why are you using int's? I would use Boolean.
Either way read this.
You are calling two different types of methods not sure if they updated java to include this syntactic sugar yet. I've been out of the loop but.
playRoom();
This function should have a set currentRoom and an entryDoor in the method by default.
Then overload it with.
playRoom(string currentRoom , string entryDoor);
Try removing the return statement from the if condition.

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.