Table as parameter in C function called from Lua - c++

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

Related

how to have lua call a c++ function that returns multiple values to lua

my code (partial)
c++:
lua_register(L, "GetPosition", lua_GetPosition);
int lua_GetPosition(lua_State* L)
{
Entity e = static_cast<Entity>(lua_tointeger(L, 1));
TransformComponent* trans = TransformComponentPool.GetComponentByEntity(e);
if (trans != nullptr)
{
lua_pushnumber(L, trans->transform->position.x);
lua_pushnumber(L, trans->transform->position.y);
lua_pushnumber(L, trans->transform->position.z);
}
else
{
lua_pushnumber(L, 0);
lua_pushnumber(L,0);
lua_pushnumber(L, 0);
LOG_ERROR("Transform not found");
}
return 1;
}
lua:
local x = 69
local y = 69
local z = 69
x,y,z = GetPosition(e)
print("xyz =",x,y,z)
I expected "xyz = 1.0 1.0 1.0"
I got "xyz = 1.0 nil nil"
What's the right way to do this so lua sees all return values?
When Lua calls your function it will check its return value to find out how many values it should fetch from the stack. In your case that's 1. How else would Lua know how many of the pushed values you want to return?
From Lua 5.4 Reference Manual 4.6 Functions and Types:
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). So, when the
function starts, lua_gettop(L) returns the number of arguments
received by the function. 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 in C the number of
results. Any other value in the stack below the results will be
properly discarded by Lua. Like a Lua function, a C function called by
Lua can also return many results.
As an example, the following function receives a variable number of
numeric arguments and returns their average and their sum:
static int foo (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
lua_Number sum = 0.0;
int i;
for (i = 1; i <= n; i++) {
if (!lua_isnumber(L, i)) {
lua_pushliteral(L, "incorrect argument");
lua_error(L);
}
sum += lua_tonumber(L, i);
}
lua_pushnumber(L, sum/n); /* first result */
lua_pushnumber(L, sum); /* second result */
return 2; /* number of results */
}

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.

Passing a Vector<struct> to Lua table

I would like improve my code below by sending a C++ Pre-formatted Lua Table:
int GetCategory(lua_State* L)
{
uint32 Type = CHECKVAL<int>(L, 1);
lua_newtable(L);
int tbl = lua_gettop(L);
uint32 counter = 1;
// Struct CT { string CategoryBrandName, CategoryName }; > Vector<CT>
auto list = sManagerMgr->GetAll();
// Hack modify this to send a metatable/UserData/Table whatever is called
for (auto& elem : list)
{
switch (Type)
{
case 1:
lua_pushstring(L, elem->CategoryBrandName);
break;
case 2:
lua_pushstring(L, elem->CategoryName);
break;
}
lua_rawseti(L, tbl, counter);
counter++;
}
lua_settop(L, tbl);
return 1;
}
Basically,
lua_newtable pushes a table to the lua stack,
lua_gettop will take the top index, so the index where the table is at.
Then lua_pushstring(L, ELEMENT); lua_rawseti(L, tbl, counter); will put the ELEMENT to the table at the index tbl we got with gettop. The index of the element is the value of counter.
But The issue here is that i'm forced to call twice the fonction GetCategory to fill it as follow in my .lua file.
table.insert(Group, { GetCategory(1), GetCategory(2) });
Current Use :
print(i, Group(1)[i], Group(2)[i]);
So.. I would prefer to call it once and get something like this directly :
local Group =
{
[1] = { "elem->CategoryBrandName[1]", "elem->CategoryName[1]" },
[2] = { "elem->CategoryBrandName[2]", "elem->CategoryName[2]" }
--etc
};
I've tried filling elem into an 2D Array[1][2] and then pushing Array unsuccessfully
I've made a lot of research about Table, Metatables, MultiDimentional Arrays etc but I couldn't find something that would fit my need or works.
Does anyone has a solution ?
Why don't you just have your function return both values? Then you could just write
local Group = { GetCategories }
I am no expert with the C API, but I think this could be done fairly easily by just calling lua_newtable(L), so something like this:
int GetCategories(lua_State* L) {
lua_settop(L, 0);
// Discard arguments so we don't have to save the top of the stack
// and can just use numbers instead (see following lines)
lua_newtable(L); // Index 1 on stack
lua_newtable(L); // Index 2 on stack
// Do your magic
lua_settop(L, 2); // Get rid of your temp variables
return 2; // number of values we return in Lua
}
Optimization hint: you can use lua_createtable and tell it how many elements each of the tables will have so Lua can pre-allocate some memory for it.
EDIT: I just noticed this, but in your code:
for (auto& elem : list) {
switch (Type) {
case 1:
lua_pushstring(L, elem->CategoryBrandName);
break;
case 2:
lua_pushstring(L, elem->CategoryName);
break;
}
lua_rawseti(L, tbl, counter);
counter++;
}
You just keep pushing values to the stack. This may, for long vectors, overflow the stack (sooner rather than later), leading to trouble. A better approach would be to 1) push to stack 2) insert into table 3) pop them back off:
// Modified for my suggested implementation that returns
// two tables. They can easily be turned around here.
for (auto& elem : list) {
lua_pushstring(L, elem->CategoryBrandName);
lua_rawseti(L, 1, counter++);
lua_pop(L, 1);
lua_pushstring(L, elem->CategoryName);
lua_rawseti(L, 2, counter++);
lua_pop(L, 1);
}
It's always a good idea to be aware of what is and what isn't on the stack. Saving some memory can not only improve performance, but also avoid potential problems due to (Lua) stack overflow.
One last detail: You don't need ; in Lua, and it is considered bad style to use them unless you have two statements in one line print('more readable'); print('like this').
In case if anyone is looking to something similar, here's how I managed by using lua_createtable. It works as intended, It may need some improvement thought.
int GetCategory(lua_State* L)
{
int counter = 1;
int MaxListSize = 2;
auto Categories = sManagerMgr->GetAll();
lua_createtable(L, Categories.size(), 0);
for (auto& elem : Categories)
{
vector<string> list;
list.reserve(MaxListSize);
list.emplace_back(elem->CategoryBrandName);
list.emplace_back(elem->CategoryName);
Macro::Push(L, counter); // custom
lua_createtable(L, 0, MaxListSize);
for (int i = 1; i <= MaxListSize; i++)
{
Macro::Push(L, list.at(i - 1)); // custom
lua_rawseti(L, -2, i);
}
lua_settable(L, -3);
list.clear();
counter++;
}
return 1;
}
Would produce an output similar to
local Group =
{
[1] = { "elem->CategoryBrandName[1]", "elem->CategoryName[2]" },
[2] = { "elem->CategoryBrandName[1]", "elem->CategoryName[2]" }
--etc
};

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.

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.