Simple lua example of passing a table to c/c++ - c++

I am struggling finding and understanding how to pass a table from lua to c++
What I have:
Lua File:
-- lua script for sending table data
io.write("lua table to send")
tableexample = {x = 1, y = 2, z = 100}
return tableexample
c/c++ side
L = lua_open();
luaL_openfile(L, "luafile");
... call the function...
luaLdofile(L, luafile);
int result;
result = lua_pcall(L,0, LUA_MULTRET,0);
if(result){
fprintf(stderr, "failed %s\n", lua_tostring(L,-1));
if(lua_istable(L,-1)){
lua_gettable(L,-1);
x = lua_tonumber(L,-2);
y = lua_tonumber(L,-3);
z = lua_tonumber(L,-4);
}
else
printf("fail");
the result comes back with a failure "attempt to call a table value"
I've looked at many different tutorials/examples but haven't found a simple tutorial that doesn't have 100 other things going on and am getting myself confused
some references that are similar - but too complex for what I am looking for
Iterating through a Lua table from C++?

Your use of lua_gettable is wrong.
Have a look at the Lua manual:
void lua_gettable (lua_State *L, int index);
Pushes onto the stack the value t[k], where t is the value at the
given index and k is the value at the top of the stack.
This function pops the key from the stack (putting the resulting value
in its place).
In your example, the value at the top of the stack is the table itself, so you are doing the equivalent of a tableexample[tableexample] lookup.
The shortest way to achieve what you want is to use lua_getfield instead, which allows accessing table elements by string keys:
lua_getfield(L, -1, "x");
Alternatively, push a Lua value of type string on the stack first and use lua_gettable for the lookup.
lua_pushliteral(L, "x");
lua_gettable(L, -2);
Don't forget to keep your stack balanced. Every value that you push on the stack needs to be popped properly. The Lua manual specifies how each API call changes the stack by the numbers in the \[ \] brackets on the right side of each function name.

Related

How Create Lua Table of Userdata metatable using C API

I Want Create a Table Like This,
a Table contains Player Userdata
PlayerList = { player1, player2, ...}
small part my code like this
int Player::GetLocal(lua_State* L){
auto p = lua_newuserdata(L, sizeof(NativePlayer));
new (p) NativePlayer();
p->data = global::get_local();
luaL_getmetatable(L, "PlayerMetaTable");
lua_setmetatable(L, -2);
return 1;
}
int Player::ToList(lua_State* L) {
lua_newtable();
GameLogic* gameLogic = global::get_game_logic();
for (auto it = gameLogic->PlayerList.begin(); it != gameLogic->PlayerList.end(); ++it) {
lua_pushinteger(L, index);
auto p = lua_newuserdata(L, sizeof(NativePlayer));
new (p) NativePlayer();
p->data = *it;
luaL_getmetatable(L, "PlayerMetaTable");
lua_setmetatable(L, -4);
lua_settable(L, -3);
}
return 1;
}
the problem is the data type in the table is not NativePlayerMetatable
instead PlayerList Becomes NativePlayerMetatable
I want the PlayerListto remain a table, and its members are NativePlayerMetatable
currentPlayer = Player.GetLocal()
PlayerList = Player.ToList()
print(currentPlayer)
print(PlayerList[0])
print(PlayerList[1])
print(PlayerList)
Output:
PlayerMetaTable: 000002607203F590
userdata: 0000026079E901D8
userdata: 0000026079EA1A08
PlayerMetaTable: 000002607203F7FF
The Output I want
PlayerMetaTable: 000002607203F590
PlayerMetaTable: 0000026079E901D8
PlayerMetaTable: 0000026079EA1A08
table: 000002607203F7FF
At the point where you call lua_setmetatable(L, -4) in Player::ToList, you have added four objects to the stack. When things are added to the stack, they go on top. We can look backwards to see what's on the stack. Starting from the top, we have first the metatable pushed by luaL_getmetatable, second the player userdata pushed by lua_newuserdata, third the index pushed by lua_pushinteger, and fourth the player list pushed by lua_newtable. (And, none of these values have be popped from the stack yet.)
(In general, we might worry about additional values pushed by previous iterations of the loop, but your loop pushes three values and also pops three values, so no values from any previous iteration will remain -- this is usually good, as a loop that pushes more values than it pops might eventually overflow unless you grow the stack, and a loop that pops more values than it pushes might underflow.)
Negative indices count backwards from the top of the stack. So, again at the point of the lua_setmetatable(L, -4) call, -1 would refer to the metatable, -2 to the player userdata, -3 to the index, and -4 to the player list. Because you are passing -4, you are assigning the metatable to the player list. To assign the metatable to the player userdata, you would use index -2 instead.
If you find negative indices hard to keep track of, you could consider using positive indices instead. When using positive indices, you may not want to use numbers directly as you may not know the size of the stack to begin with. (For example, in a C function called directly via Lua, which is probably the case for the functions you have exhibited, the stack will initially contain any arguments that were passed to the function, and it would be sloppy to assume that no parameters were passed without checking, even if you don't expect parameters.) You can use lua_gettop to find the index of the element at the top of the stack. E.g.
lua_newtable(L);
const int list_index = lua_gettop(L);
... and later ...
auto p = lua_newuserdata(L, sizeof(NativePlayer));
const int player_index = lua_gettop(L);
new (p) NativePlayer();
p->data = *it;
luaL_getmetatable(L, "PlayerMetaTable");
lua_setmetatable(L, player_index);
lua_settable(L, list_index);
But, this does not mean you can ignore the stack altogether as, e.g. you need to know the top of the stack when you return (although a sloppy implementation could push another copy of the value it wants to return just before returning), and many of the API functions like lua_setmetatable and lua_settable deal directly with values on top of the stack (even if they also take indices).

Accessing a Lua table within a table from C++ side

I'm trying to transfer a table where there may be nested tables from the lua and write to a .ini file. But I just can not how I need to go on the stack to get data from nested tables. This code does not work as it should. Function setData work not correctly. What could be the problem?
C++ code
int sasl::LuaUtilities::luaWriteIni(LuaState inOutState)
{
string path;
boost::property_tree::ptree iniTree;
LUA_CHECK_NARG_EQ(inOutState, 2);
LUA_GET_PARAM(inOutState, 1, path);
int nargs = lua_gettop(inOutState);
for (int i = 1; i <= nargs; i++) {
if (lua_istable(inOutState, nargs)) {
setData(inOutState, nargs, iniTree);
}
}
return 0;
}
void sasl::LuaUtilities::setData(LuaState inOutState, int index, boost::property_tree::ptree & inIniTree)
{
// Push an initial nil to init lua_next
lua_pushnil(inOutState);
// Parse the table at index
while (lua_next(inOutState, index))
{
if (lua_istable(inOutState, index))
{
setData(inOutState, index, inIniTree);
}
else
{
string key = lua_tostring(inOutState, -2);
string value = lua_tostring(inOutState, -1);
}
// Pop value, keep key
lua_pop(inOutState, 1);
}
return;
}
Lua code
t = {}
local fileName = findResourceFile("test.ini")
t = readINI(fileName)
writeINI(fileName, t) --this not work in c++ side
There are two problems. lua_istable(inOutState, index) is wrong, because index is not the value of the key retrieved by next. That index is always the table you're iterating over. So you'll infinitely recurse over the same table when you call setData with that index.
In fact, passing index to setData itself is almost certainly wrong. Or at least, it's probably not right. You want to use relative indices here, but calling next pushes an extra value onto the stack.
What you probably want to do is have setData assume that the table to iterate over is at index -1 (ie: the top of the stack). That way, you're just calling lua_next(state, -2) (this is -2 because the key to get the next one for is at the top). And when you recursively call setData for a table value, you don't need to provide an index, because the table value is already at the top of the stack.
The second problem is that you never write the key/value pairs. You also never check to see if the value is something which can be converted to a string. It could be a Lua userdata.

Table as parameter in C function called from Lua

I'm dealing with Lua for a longer time but there is one point I can't achieve.
In a C function which is called from Lua I'm able to read a global Lua table using the table name like this:
C:
// get table
lua_getglobal(L, "tableName");
if (!lua_istable(L, -1))
break;
// count entries in table
ULONG numEntries = 0;
lua_pushnil(L);
while(lua_next(L,-2))
{
numEntries++;
lua_pop(L, 1);
}
but if I have a lua function which calls a C function like this:
Lua:
luaTable = { }
luaTable.Param1 = Value1
luaCallC("This is a Text", luaTable)
How do I access the table argument?
C:
// get table
// ???
// count entries in table
ULONG numEntries = 0;
lua_pushnil(L);
while(lua_next(L,-2))
{
numEntries++;
lua_pop(L, 1);
}
Arguments to a CFunction are pressed onto the virtual stack in the order that they are provided, and it is simply up to you to do the error checking required before you operate on these values.
Lua 5.3 Manual ยง4.8 - lua_CFunction:
In order to communicate properly with Lua, a C function must use the following protocol, which defines the way parameters and results are passed: a C function receives its arguments from Lua in its stack in direct order (the first argument is pushed first).
[ ... ]
The first argument (if any) is at index 1 and its last argument is at index lua_gettop(L). To return values to Lua, a C function just pushes them onto the stack, in direct order (the first result is pushed first), and returns the number of results
An example of exhaustively checking the number of elements in a table, with an arbitrary first argument.
int count (lua_State *L) {
luaL_checktype(L, 2, LUA_TTABLE);
puts(lua_tostring(L, 1));
size_t ec = 0;
lua_pushnil(L);
while (lua_next(L, 2)) {
lua_pop(L, 1);
ec++;
}
lua_pushinteger(L, (lua_Integer) ec);
return 1;
}
After registering the function for use in Lua:
count('foo', { 'a', 'b', 'c' }) -- 3

calling a c++ function from lua passes less arguments

So the function is like :
send_success(lua_State *L){
MailService *mls = static_cast<MailService *>(lua_touserdata(L, lua_upvalueindex(1)));
Device *dev = static_cast<Device *>(lua_touserdata(L, lua_upvalueindex(2)));
int numArgs = lua_gettop(L);
TRACE << "Number of arguments passed is = " << numArgs;
/* here I do some operation to get the arguments.
I am expecting total of 5 arguments on the stack.
3 arguments are passed from function call in lua
and 2 arguments are pushed as closure
*/
string one_param = lua_tostring(L, 3, NULL)
string two_param = lua_tostring(L, 4, NULL)
string other_param = lua_tostring(L, 5, NULL)
}
Now pushing this function on lua stack, I have done following
lua_pushstring(theLua, "sendSuccess");
lua_pushlightuserdata(theLua, (void*) mls);
lua_pushlightuserdata(theLua, (void*) this);
lua_pushcclosure(theLua, lua_send_success,2);
lua_rawset(theLua, lua_device); // this gets me device obj in lua
calling it from lua , i would do
obj:sendSuccess("one param","second param","third param")
But when i check for the number of arguments. It should give 5 arguments. Instead only 4 arguments are passed.
I did some testing whether the two objects i pass a light used data are passed correctly. They are passed correctly.
Only thing missing here is, that one parameter is missing which is passed from lua side.
Also i tried pushing only one object and it worked correctly. so I am not sure if I am messing up with argument numbering somewhere
Please tell your opinions
The user-data objects you create as part of the closure are not passed as arguments to the function, they placed in another location in the state.
That means the offsets you use to get the arguments with lua_tostring are wrong.
OK. SO the thing is
lua_pushclosure keeps the userdata in a separate space on lua_stack. Inside that stack, the offset 1 and 2 represent first and 2nd object
lua_pushlightuserdata(theLua, (void*) mls);
lua_pushlightuserdata(theLua, (void*) this);
lua_pushcclosure(theLua, lua_send_success,2);
But after that i was going to the third 3rd, assuming I had already accessed 2nd location. But that is wrong. The right thing to do is to consider that pushclousure takes only one space on stack irrespective of how many times the lightuserdata is pushed and remaining params can be accessed by starting from the 2nd offset.. so the below code works for me:
string one_param = lua_tostring(L, 2, NULL)
string two_param = lua_tostring(L, 3, NULL)
string other_param = lua_tostring(L, 4, NULL)

Accessing a Lua table within a table from C++

I have a Lua function that returns a dictionary table, and one of the values that's put into the returned table is another table as demonstrated in the following Lua function.
function tableWithinTable()
local ret = {}
ret["a"] = 1
ret["b"] = {1,2,3}
ret["c"] = 3
return ret
end
How would I go about accessing that inner table?
I know I can get to the table, because I can enter the following if statement. My current attempt at reading the table is included as well.
lua_pushstring("b");
lua_gettable(lua,1);
if(lua_istable(lua,-1))
{
//whatever is in here is executed.
lua_pushnumber(lua,1);
lua_gettable(lua,-1); //crashes to desktop here
std::cout << lua_tonumber(lua,-1) << std::endl;
lua_pop(lua,1);
}
I'm pretty sure there's an easy solution to this, but I'm totally stumped. Any help would be much appreciated.
After you push the index, the table is one more slot away in the stack. So something like this should work:
if(lua_istable(lua,-1)) {
lua_pushnumber(lua,1);
lua_gettable(lua,-2);
...
It is generally easier to use functions lua_getfield (for string indexes) or lua_rawgeti (for numerical indexes) than the raw lua_gettable function.
In particular, that avoids the extra value on the stack that causes your indexing error.
The example could be rewritten as :
lua_getfield(lua, 1, "b");
if(lua_istable(lua, -1))
{
lua_rawgeti(lua, -1, 1);
std::cout << lua_tonumber(lua,-1) << std::endl;
lua_pop(lua, 1);
}