How Create Lua Table of Userdata metatable using C API - c++

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

Related

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.

What happens if I push userdata twice using the same key?

I would like to know what happens if I push the lightuserdata into the registry twice using the same key.
My Code:
MyData *x, *y; //let's say these are valid pointers
lua_pushstring(L, "my_data");
lua_pushlightuserdata(L, static_cast<void *>(x));
lua_settable(L, LUA_REGISTRYINDEX);
lua_pushstring(L, "my_data");
lua_pushlightuserdata(L, static_cast<void *>(y));
lua_settable(L, LUA_REGISTRYINDEX);
lua_pushstring(L, "my_data");
lua_gettable(L, LUA_REGISTRYINDEX);
MyData *data = static_cast<MyData *>(lua_touserdata(L, -1));
//would the data be x or y?
Would the previously pushed pointer (x) be replaced with the new one (y)?
ADDED: And is there a way to check the list of keys that are currently registered?
The Lua registry is an ordinary Lua table. Your code is equivalent to
registry.my_data = x
registry.my_data = y
So, yes, after these two lines the value of registry.my_data is the value of y.

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.

Circular Traversal of a Linked List

So I'm having some trouble with one of my functions. The program (in C++) plays a game, and there are so many players sitting at the table. Every time my play function is called, it should display to the console a player in the game. Every time it is called it should display the players in sequence. After it hits the last player it will start back at the beginning of the list/table.
void CircleList::play()
LinkedListOfPlayersNode *p=(*pFront).pNext;
if (p->pData!=NULL)
{
cout<<p->pData->getName()+" takes a turn\n";
pLastPlayer = pLastPlayer->pNext;
}
else
{
cout<<"There are no players. Please ADD a player.\n";
}
}
So lets say that that we add A, B, and C. When you use the PLAY command, C should take a turn, then B then A. As of right now with the above code, it will display that C Takes a Turn, however, it crashes right after that.
So what is wrong with my code? Is there a better way to write this?
I'm pretty sure you want that traversal to look something like this:
LinkedListOfPlayersNode *p = pNextPlayer ? pNextPlayer : pFront;
if (p && p->pData) // not sure why pData is also dynamic. look into that.
{
cout<<p->pData->getName()+" takes a turn\n";
pNextPlayer = p->pNext;
}
else
{
cout<<"There are no players. Please ADD a player.\n";
}
Each time a player's turn comes up, they take it, and the next player is that player's next pointer. When your last player at the table takes a turn, p->pNext will be null, and the next invoke will reset to the head of the list.
At least thats where I think you're trying to get. Prime this with either pNextPlayer being set to pFront or NULL; makes no difference.
Lets say you initially define pnext = NULL in while creating nodes.
So according to your code;
if (p->pData!=NULL)
{
cout<<p->pData->getName()+" takes a turn\n";
pLastPlayer = pLastPlayer->pNext;
}
else
{
cout<<"There are no players. Please ADD a player.\n";
}
There are 2 scenarios according to my assumptions:
1. p and pLastPlayer are the same ... so after the list is over. p will be equal to NULL, your code will crash as it tries to de-reference p->pData.
2. p and pLastPlayer are different ... So after the list pLastPlayer ends, the code will crash at pLastPlayer = pLastPlayer -> pNext(); as you are probably de-referencing garbage or NULL value;
You need to check for NULL pointers at some point, either in p -> pData != NULL or pLastPlayer = pLastPlayer -> pNext();
You are in C++, you are better off with a std::list than writing your own implementation.
Something's bugging me: Why are you starting your loop with "p = (*pFront).pNext" instead of starting it straight with "p = pFront"?
How are you adding your nodes to your list? You should be echo-ing "A takes a turn" before "C takes a turn".
If you are indeed adding to the front of your list (and your list is doubly-linked), you should call pPrevious instead of pNext.

Lua Accessing a table's Keys and Values

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.