C++ Lua embedding, pushing table to a function that on arguments - c++

so i was trying to push a table on a function inside arguments
*lua
function test1(varlist)
print(varlist[1])
print(varlist[2])
print(varlist[3])
end
addHook("string", "string2", test1)
*cpp
static int lua_addHook(lua_State* L) {
if (lua_isstring(L, 1) && lua_isstring(L, 2) && lua_isfunction(L, 3)) {
lua_newtable(L);
lua_newtable(L);
for (size_t i = 0; i < 3; ++i) {
lua_pushinteger(L, i + 1);
lua_pushstring(L, string("string varlist: " + to_string(i)).c_str());
lua_settable(L, -3);
}
if (lua_pcall(L, 1, 0, 0) != 0) {
printf("error: %s\n", lua_tostring(L, -1));
lua_pop(L, 1);
}
}
return 1;
}
so it should printing
string varlist: 0
string varlist: 1
string varlist: 2
but i keep getting error "attempt to call a table value"
u know what the problem is?

The stack looks like this at the point of lua_pcall:
table (constructed by the loop above) # STACK TOP and arg1 to function call
table (empty) # interpreted as the function to call
function test1
string "string1"
string "string"
Getting rid of one of the lua_newtable calls should fix it.

Related

Problem with lua_isstring iterating for table in table

I try to write a table to an ini file, everything worked until I added a line lua_tostring(L, -2) then lua_next(L, -2) began to give out an error. How this line can influence, in my understanding, I just take the value from the stack and no more. How I can fix it?
{
// Push an initial nil to init lua_next
lua_pushnil(inOutState);
// Parse the table at index
while (lua_next(inOutState, -2))
{
if (lua_isstring(inOutState, -1)) {
string key = lua_tostring(inOutState, -2);
string value = lua_tostring(inOutState, -1);
inIniTree.put(suffix + key, value);
}
else if (lua_istable(inOutState, -1)) {
suffix += lua_tostring(inOutState, -2); !!!!!! without this line function is working well !!!!!!!
setDataInIni(inOutState, inIniTree, suffix);
}
// Pop value, keep key
lua_pop(inOutState, 1);
}
return;
}
lua_tostring() replaces value in stack if value is not of type string. It means you changed key for lua_next(). You must copy value with lua_pushvalue() and then convert it to string.
if (lua_isstring(inOutState, -1)) {
lua_pushvalue(inOutState, -2);
string key = lua_tostring(inOutState, -1);
lua_pop(L, 1);
string value = lua_tostring(inOutState, -1);
inIniTree.put(suffix + key, value);
}
else if (lua_istable(inOutState, -1)) {
lua_pushvalue(inOutState, -2);
suffix += lua_tostring(inOutState, -1);
lua_pop(L, 1);
setDataInIni(inOutState, inIniTree, suffix);
}

Using lua_call a lot of times in c++ function

first of all I'm sorry for my english.
My question is about how to use lua_call more than one time in C++ function. I have a program that uses lua as primary language, but it accept c++ plugins to add functionalities. I want to call a LUA function from c++, and call that c++ function in LUA Runtime.
I want to write a c++ function with a progress while is working, then pass this progress to a LUA function which is responsible to show that progress to user.
For now I've a test function in LUA:
function ShowText(text)
Dialog.Message("Hi", text);
return true;
end
and a c++ function:
static int Test(lua_State *L){
lua_pushstring(L, "Hi There");
lua_call(L, 1, 1);
lua_pushstring(L, "Again");
lua_call(L, 1, 1);
return 0;
}
Then i call this function from LUA using:
Test.Test(ShowText);
All works fine with the first lua_call but then the LUA pile is cleared, function dissapear and the second lua_call try to use the return boolean of first call instead function.
i want something like this:
static int Test(lua_State *L){
int total = 10;
for (int j; j<total; j++){
lua_pushnumber(L, j);
lua_pushnumber(L, j);
lua_call(L, 2, 1);
bool continue = IRLUA_PLUGIN_CheckBoolean(L, -1);
lua_pop(L, 1); //Delete the last boolean
if (continue == false){
break;
}
}
return 0;
}
and in LUA:
function ShowProgress(actual, final)
local percent = (actual/final)*100;
Dialog.Message("Working", "I'm in "..actual.." from "..final.." ("..percent.."%)");
return true;
end
NOTES:
Dialog.Message is a function of the program tha i'm using to show a message. Is like MessageBox(NULL, Text, Title, MB_OK); in c++.
IRLUA_PLUGIN_CheckBoolean is a function of plugin SDK that check if argument is booleand and return its value, or return an error if not.
I can do it with lua_getfield(L, LUA_GLOBALSINDEX , "FunctionName");, but is not what i want.
Someone knows how to do it?
You have understood the problem well. Here is how you fix it.
In your first example, lua_call pops the function from the stack so you need to duplicate it first. Also, the boolean returned by the function is useless, so you need to pop it or just not ask it to lua_call by setting the last argument to 0:
static int Test(lua_State *L) {
lua_pushvalue(L, 1); /* duplicate function */
lua_pushstring(L, "Hi There");
lua_call(L, 1, 0);
lua_pushstring(L, "Again");
lua_call(L, 1, 0);
return 0;
}
Now applying that to your second example:
static int Test(lua_State *L) {
int total = 10;
for (int j = 0; j<total; j++) {
lua_pushvalue(L, 1); /* duplicate function */
lua_pushnumber(L, j);
lua_pushnumber(L, total);
lua_call(L, 2, 1);
bool keep_going = IRLUA_PLUGIN_CheckBoolean(L, -1);
lua_pop(L, 1); /* pop the boolean */
if (keep_going == false) {
break;
}
}
return 0;
}
(I have fixed a few other issues with your code: the second number passed should probably be total, not j, you don't want to use continue as a variable name...)

Reading Lua nested tables in C++

I'm creating a C/C++ function which will be called from Lua. My function must call a library function who's signature is like this:
void libFunction( int val1, int val2, tSETTINGS * pSettings );
I'm given these C/C++ structs:
typedef struct
{
int cmd;
int arg;
} tCOMMAND;
typedef struct
{
int numberCommands;
int id;
tCOMMAND commands[1];
} tSETTINGS;
Maybe my thinking is all wrong on this, but from Lua I'm calling like this:
id = 42
val1 = 1
val2 = 2
cmd1 = { 3, 4 }
cmd2 = { 5, 6 }
commands = { cmd1, cmd2 }
settings = { #commands, id, commands }
mycfunction( val1, val2, settings )
I'm sure that I'm still not understanding the Lua stack as referenced from C++, since what I'm trying just doesn't work. My solution is able to retrieve val1, val2, #commands and id, but when I try to retrieve commands[0] and commands[1] I get {1, 2} and {2, 42} respectively.
My C++ is essentially like this (for this sample I'm discarding the values). I've already retrieved val1 and val2:
int stkNdx = 1;
lua_rawgeti(L, 3, stkNdx++ );
int numcmds = lua_tointeger(L, -1); // this successfully retrieves numberCommands 2
lua_pop(L, 1);
lua_rawgeti(L, 3, stkNdx++ );
int id = lua_tointeger(L, -1); // this successfully retrieves id 42
lua_pop(L, 1);
lua_pushvalue(L, -1 );
lua_pushnil(L);
int cmdNbr = 0;
for( lua_next(L, -2); cmdNbr < numcmds; cmdNbr++ )
{
lua_pushvalue(L, -2);
int cmd = lua_tointeger(L, -1);
int arg = lua_tointeger(L, -1);
lua_pop(L, 2);
lua_next(L, -2);
}
lua_pop(L, 1);
I've tried various permutations of lua_rawgeti() followed by lua_tonumber() and lua_pop(), with basically the same result.
This seems similar to this question, and my solution is modeled after that with no success.
Experimenting more I inserted this:
lua_pushnil(L);
while( lua_next(L, -2) )
{
if( ! lua_istable(L, -1) )
{
int v = lua_tointeger(L, -1);
}
lua_pop(L, 1);
}
This loop executes 4 times. The first 2 times the values 2 and 42 are assigned to v. The next 2 iterations skip the assignment (lua_istable returned true). So it seems that although I've already retrieved numcmds and id, they're still there on the stack. I also clearly don't understand how to iterate over the subtables when they're encountered.
Lua table indices range from [1 .. N] instead of [0 .. N-1].
Your loop should be:
int cmdNbr = 1;
for( lua_next(L, -2); cmdNbr <= numcmds; cmdNbr++ )
{
...
}
or as I prefer it:
lua_rawgeti(L, 3, 2 );
int id = lua_tointeger(L, -1); // this successfully retrieves id 42
lua_pop(L, 1);
lua_rawgeti(L, 3, 3);
{
// commands table at stack top
size_t N = lua_objlen(L,-1); // size of the table
for (int i = 1; i <= N; ++i)
{
lua_rawgeti(L,-1, i); // cmd# at stack top
{
lua_rawgeti(L,-1,1); // first entry
int cmd = lua_tointeger(L,-1);
lua_pop(L,1);
lua_rawgeti(L,-1,2); // second entry
int arg = lua_tointeger(L,-1);
lua_pop(L,1);
}
lua_pop(L, 1); // pop cmd#
}
}
lua_pop(L, 1); // pop commands table
Note that, with the function lua_objlen(L,idx), it's not necessary to pass numcmds.

Get lua table entry from C via integer key

I am currently using the following code to get a value from a table (cstring = const char*):
template<>
cstring luaTable::get(cstring name) {
prep_get(name); // puts table[name] at -1 in stack
cstring result;
if(!lua_isstring(L, -1)) {
report(name, "is not a string");
result = "";
}
else {
result = lua_tostring(L, -1);
}
lua_pop(L, 1);
return result;
}
void luaTable::prep_get(cstring name) {
lua_pushstring(L, name); // name at -1, table at -2
lua_gettable(L, -2);
// table[name] is now at position -1 in stack
}
This works perfectly for tables of form table = {a=10, b=2}. How can I modify it to get values from tables without keys such as table = {10, 2}?
I'm sure I'm missing something simple but can't seem to find the answer.
Thanks in advance,
Ben
Edit: added some pops
Okay sorry to answer my own question so soon - but a quick flash of inspiration lead to:
void luaTable::prep_get(cstring name) {
lua_pushstring(L, name); // name string at -1
if(lua_isnumber(L, -1)) { // call prep_get("i") for ith element etc
int key = lua_tonumber(L, -1);
lua_pop(L, 1); // remove the name string from -1
lua_pushnumber(L, key); // push name number to -1
}
lua_gettable(L, -2);
// result is now at position -1 in stack
}
which works as desired.
#user1483596 I don't think that solution would work. lua_isnumber will only return true if the value is of type number, and you just pushed a string, so it will always return false.
Instead, try something like this:
void luaTable::prep_get(cstring name) {
int num = strtol(name, 0, 0);
if (num > 0) {
lua_pushnumber(L, num);
} else {
lua_pushstring(L, name);
}
lua_gettable(L, -2);
}
Bear in mind though that it won't handle a special case. In Lua a[1] and a["1"] are different. If you use this function, you'll always treat numbers as array indices, even if they're not.
If you want to differentiate both cases, then you could overload prep_get and take a number.

Safe Lua invoke C++ registered function

Hey,everyone! I've a C++ app embedded Lua as script. A non-programmer edits the Lua script, then the C++ app invoke the Lua script and the Lua script also invokes C++ registered function.
I use Luaplus to do the above job. My question is: when the script-editor makes mistakes such as misspelling the the parameter, the C++ app crashes! What can I do to prevent this happening? thanks
Look at lua_cpcall and lua_pcall. They both allow protected function calls of lua in c. If they return a non-negative number then the call failed and the lua stack contains only the error string. In cpcalls case the stack is otherwise unmodified. For pcall you'll need to look at lua_pushcclosure to invoke a cfunction safely.
What you do is this: you create a c function with all of the lua_* calls you want, such as loadfile and dofile. You call this function using lua_cpcall or lua_pushcclosure amd lua_pcall. This allows you to detect if an error occured in t
he function you passed to cpcall.
Examples:
function hello() {
string hello_ = "Hello Lua!";
struct C {
static int call(lua_State* L) {
C *p = static_cast<C*>(lua_touserdata(L,-1));
lua_pushstring(L, p->str.c_str() );
lua_getglobal(L, "print");
lua_call(L, 1, 0); //ok
lua_pushstring(L, p->str.c_str() );
lua_getglobal(L, "notprint");
lua_call(L, 1, 0); //error -> longjmps
return 0; //Number of values on stack to 'return' to lua
}
const string& str;
} p = { hello_ };
//protected call of C::call() above
//with &p as 1st/only element on Lua stack
//any errors encountered will trigger a longjmp out of lua and
//return a non-0 error code and a string on the stack
//A return of 0 indicates success and the stack is unmodified
//to invoke LUA functions safely use the lua_pcall function
int res = lua_cpcall(L, &C::call, &p);
if( res ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
//Error hanlder here
}
//load a .lua file
if( (res=luaL_loadfile(L, "myLuaFile.lua")) ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
//res is one of
//LUA_ERRSYNTAX - Lua syntax error
//LUA_ERRMEM - Out of memory error
//LUE_ERRFILE - File not found/accessible error
}
//execute it
if( (res=lua_pcall(L,0,0,0)) ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
// res is one of
// LUA_ERRRUN: a runtime error.
// LUA_ERRMEM: memory allocation error.
// LUA_ERRERR: error while running the error handler function (NULL in this case).
}
// try to call [a_int,b_str] = Foo(1,2,"3")
lua_getglobal(L,"Foo");
if( lua_isfunction(L,lua_gettop(L)) ) { //Foo exists
lua_pushnumber(L,1);
lua_pushnumber(L,2);
lua_pushstring(L,"3");
lua_pushvalue(L, -4); //copy of foo()
if( (res = lua_pcall(L, 3, 2, 0/*default error func*/)) ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
//error: see above
}
int a_int = (int)lua_tointeger(L,-2);
string b_str = lua_tostring(L,-1);
lua_pop(L,2+1); //2 returns, + extra copy of Foo()
}
}