Lua Accessing a table's Keys and Values - c++

I would like to read a Lua file in a level editor so I can display its data in visual format for users to edit.
If I have a Lua table like so:
properties = {
Speed = 10,
TurnSpeed = 5
}
Speed is obviously the key and 10 the value. I know I can access the value if I know the key like so (provided the table is already on the stack):
lua_pushstring(L, "Speed");
lua_gettable(L, idx);
int Speed = lua_tointeger(L, -1);
lua_pop(L, 1);
What I want to do is access the key's name and the corresponding value, in C++. Can this be done? If so how do I go about it?

This is covered by the lua_next function, which iterates over the elements of a table:
// table is in the stack at index 't'
lua_pushnil(L); // first key
while (lua_next(L, t) != 0)
{
// uses 'key' (at index -2) and 'value' (at index -1)
printf("%s - %s\n", luaL_typename(L, -2), luaL_typename(L, -1));
// removes 'value'; keeps 'key' for next iteration
lua_pop(L, 1);
}
lua_next keys off of the, um, key of the table, so you need to keep that on the stack while you're iterating. Each call will jump to the next key/value pair. Once it returns 0, then you're done (and while the key was popped, the next wasn't pushed).
Obviously adding or removing elements to a table you're iterating over can cause issues.

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

Simple lua example of passing a table to 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.

strange behavior of lua_getfield

I have spotted strange behavior with lua_getfield() function. Please look at this code fragment, which should just get a field from table located at the stack top:
if(lua_istable(L,-1)){
lua_getfield(L,-1,"field_name");
int type = lua_type(L,-1); // returns LUA_TNIL
int field_value = lua_tointeger(L,-1); // returns 0
lua_pop(L,1);
// and now let's try iterating all table's fields:
lua_pushnil(L); // first key
while(lua_next(L, -2) != 0){
// uses 'key' (at index -2) and 'value' (at index -1)
CString key = lua_tostring(L,-2);
int type = lua_type(L,-1);
if(key == "field_name"){ //
int value = lua_tointeger(L,-1); // returns correct value!!!! (type == LUA_TNUMBER)
// ????? what the heck ????
}
// removes 'value'; keeps 'key' for next iteration
lua_pop(L, 1);
}
The question is, why lua_getfield() doesn't work, while lua_next() works perfectly?
I have used lua_getfield() tens of times with no problems and now I'm bumping my head into my keyboard...
reagards
Marcin
Problem solved. There was a problem with adjusting the number of results returned by previous call to lua_pcall. What confuses me though, is why lua_next worked correctly when lua_getfield() miserabely failed...