Lua "attempt to index a nil value" - c++

I'm trying to register a vector type with Lua, but I'm getting a strange "attempt to index a new value" error when I'm calling the addition metafunction from Lua.
Here is the code section involved. I did not include any other metafunctions (they have the same problem, the only difference is the math operator used in one of the last lines). The error seems to come from the static int LuaVector_lua___add(lua_State *L) function.
static void LuaVector_pushVector(lua_State *L, double x, double y)
{
lua_newtable(L);
lua_pushstring(L, "x");
lua_pushnumber(L, x);
lua_settable(L, -3);
lua_pushstring(L, "y");
lua_pushnumber(L, y);
lua_settable(L, -3);
lua_newtable(L);
lua_pushstring(L, "__add");
lua_pushcfunction(L, LuaVector_lua___add);
lua_settable(L, -3);
lua_setmetatable(L, -2);
}
static int LuaVector_lua___add(lua_State *L)
{
if (!lua_istable(L, 1))
luaL_error(L, "Table excepted for argument #1 LuaVector_lua___add");
if (!lua_istable(L, 2))
luaL_error(L, "Table excepted for argument #2 LuaVector_lua___add");
double x1=0, y1=0, x2=0, y2=0;
/* The error occurs somewhere between here */
lua_pushstring(L, "x");
lua_gettable(L, 1);
x1 = lua_tonumber(L, -1);
lua_pop(L, -1);
lua_pushstring(L, "y");
lua_gettable(L, 1);
y1 = lua_tonumber(L, -1);
lua_pop(L, -1);
lua_pushstring(L, "x");
lua_gettable(L, 2);
x2 = lua_tonumber(L, -1);
lua_pop(L, -1);
lua_pushstring(L, "y");
lua_gettable(L, 2);
y2 = lua_tonumber(L, -1);
lua_pop(L, -1);
/* And here */
LuaVector_pushVector(L, x1 + x2, y1 + y2);
return 1;
}
int LuaVector_lua_new(lua_State *L)
{
double x = 0;
if (!lua_isnil(L, 1))
x = lua_tonumber(L, 1);
double y = 0;
if (!lua_isnil(L, 2))
y = lua_tonumber(L, 2);
LuaVector_pushVector(L, x, y);
return 1;
}
void LuaVector_luaregister(lua_State *L)
{
lua_newtable(L);
lua_pushstring(L, "new");
lua_pushcfunction(L, LuaVector_lua_new);
lua_settable(L, -3);
lua_setglobal(L, "Vector");
}
It crashes with the code:
local vec1 = Vector.new(2, 2)
local vec2 = Vector.new(4, 4)
local vec3 = vec1 + vec2
I tried to isolate what caused it, but I couldn't determine the actual line that is faulty (though, I believe it's lua_gettable that triggers the error itself). So it could be anything, but I can't seem to figure it out.

lua_pop Pops n elements from the stack
But you have written lua_pop(L, -1) (which clears the entire stack). Change these to lua_pop(L, 1) to pop just the top of the stack.

Related

How to pass userdata from one Lua chunk to another in C++

I'm trying to get userdata from a Lua script(chunk A) in C++(through a returned variable from function in my example) and then, later pass this userdata back to Lua script(chunk B) from C++(through a function argument in my example) so the userdata can be used in chunk B as it was in the chunk A.
MyBindings.h
class Vec2
{
public:
Vec2():x(0), y(0){};
Vec2(float x, float y):x(x), y(y){};
float x, y;
};
MyBindings.i
%module my
%{
#include "MyBindings.h"
%}
%include "MyBindings.h"
main.cpp
#include <iostream>
#include <lua.hpp>
extern "C"
{
int luaopen_my(lua_State *L);
}
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaopen_my(L);
lua_settop(L, 0);
/* chunk A */
luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n"
"print(p.x)\n"
"end\n");
void *userDataPtr = nullptr;
/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
/* check the return value */
if (lua_gettop(L) - top)
{
/* store userdata to a pointer */
if (lua_isuserdata(L, -1))
userDataPtr = lua_touserdata(L, -1);
}
/* check if userDataPtr is valid */
if (userDataPtr != nullptr)
{
/* call test function */
lua_getglobal(L, "test");
lua_pushlightuserdata(L, userDataPtr); /* pass userdata as an argument */
if (lua_pcall(L, 1, 0, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
}
lua_close(L);
}
The Result I get :
[string "local vec2 = my.Vec2(3, 4)..."]:6: attempt to index a
userdata value (local 'p')
The Result I expect :
3
Is it possible to get userdata from chunk A and then pass this to chunk B so it can be used like it was in chunk A?
You're losing all information about the object's type when you get raw pointer to userdata's data and pushing it to arguments as lightuserdata. The lightuserdata even has no individual metatables.
The correct way would be to pass the Lua value as it is. Leave the original returned value on Lua stack, or copy it into other Lua container (your Lua table for temporaries, or Lua registry), then copy that value on Lua stack to pass it as an argument. That way you don't have to know anything about binding implementation. You don't even have to care if that's a userdata or any other Lua type.
Based on your code, this might look like this:
#include <iostream>
#include <lua.hpp>
extern "C"
{
int luaopen_my(lua_State *L);
}
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
/* chunk A */
luaL_dostring(L, "local vec2 = {x=3, y=4}\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n"
"print(p.x)\n"
"end\n");
/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
exit(EXIT_FAILURE); // simpy fail for demo
}
/* check the return value */
if (lua_gettop(L) - top)
{
// the top now contains the value returned from setup()
/* call test function */
lua_getglobal(L, "test");
// copy the original value as argument
lua_pushvalue(L, -2);
if (lua_pcall(L, 1, 0, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
exit(EXIT_FAILURE);
}
// drop the original value
lua_pop(L, 1);
}else
{
// nothing is returned, nothing to do
}
lua_close(L);
}
In addition to the other answer I would like to show a variant where you store the a reference to the value in the Lua registry. The advantage of this approach is that you don't have to keep the value on the stack and think about what the offset will be. See also 27.3.2 – References in “Programming in Lua”.
This approach uses three functions:
int luaL_ref (lua_State *L, int t);
Pops from the stack the uppermost value, stores it into the table at index t and returns the index the value has in that table. Hence to save a value in the registry we use
userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
int lua_rawgeti (lua_State *L, int index, lua_Integer n);
Pushes onto the stack the value of the element n of the table at index (t[n] in Lua). Hence to retrieve a value at index userDataRef from the registry we use
lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);
void luaL_unref (lua_State *L, int t, int ref);
Removes the reference stored at index ref in the table at t such that the reference can be garbage collected and the index ref can be reused. Hence to remove the reference userDataRef from the registry we use
luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);
#include <iostream>
#include <lua.hpp>
extern "C" {
int luaopen_my(lua_State *L);
}
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaopen_my(L);
lua_settop(L, 0);
/* chunk A */
luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n"
"print(p.x)\n"
"end\n");
int userDataRef = LUA_NOREF;
/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
/* check the return value */
if (lua_gettop(L) - top) {
/* store userdata to a pointer */
userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
}
/* check if userDataRef is valid */
if (userDataRef != LUA_NOREF && userDataRef != LUA_REFNIL) {
/* call test function */
lua_getglobal(L, "test");
lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);
/* free the registry slot (if you are done) */
luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);
if (lua_pcall(L, 1, 0, 0)) {
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
}
lua_close(L);
}
Maybe you want to check out the Sol2 wrapper for the Lua-C-API. It can do exactly what you want with minimal boilerplate. However, it requires C++14.
#include <iostream>
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
extern "C" int luaopen_my(lua_State *L);
int main() {
sol::state L;
L.open_libraries();
luaopen_my(L);
/* chunk A */
L.script("local vec2 = my.Vec2(3, 4)\n"
"function setup()\n"
"return vec2\n"
"end\n");
/* chunk B */
L.script("function test(p)\n"
"print(p.x)\n"
"end\n");
auto userDataRef = L["setup"]();
L["test"](userDataRef);
}

How to call functions inside package.preload in C++

I'm trying to call A:update(x) and get a returned value x + 3 in C++.
Here's my code:
#include <lua.hpp>
void main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
luaL_dostring(L, "package.preload['A'] = function () local A = {}\n"
"function A:update(x) return x + 3 end \n"
"return A end");
//call function
lua_getglobal(L, "require");
lua_pushstring(L, "A");
if (lua_pcall(L, 1, LUA_MULTRET, 0) != 0) {
std::cerr << "lua:" << lua_tostring(L, 1) << '\n';
lua_pop(L, 1);
}
int top = lua_gettop(L);
lua_getfield(L, -1, "update");
if (!lua_isfunction(L, -1))
{
std::cerr << "lua:" << lua_tostring(L, 1) << '\n';
lua_pop(L, 1);
}
lua_pushnumber(L, 5); //pass the argument 5
if (lua_pcall(L, 1, LUA_MULTRET, 0))
{
std::cerr << "lua:" << lua_tostring(L, 1) << '\n';
lua_pop(L, 1);
}
if (lua_gettop(L) - top)
{
if (lua_isnumber(L, -1))
{
std::cout << "RETURNED : " << lua_tonumber(L, -1) << std::endl;
}
}
lua_pop(L, 1); // pop 'update'
lua_pop(L, 1); // pop 'A'
lua_close(L);
}
I expect it to print RETURNED : 8 but I get the following error:
Thread 1:EXC_BAD_ACCESS (code=1, address=0x0)
How should I correct my code to work?
EDITED: It worked as soon as I change A:update(x) to A.update(x). I thought they work identically except I can use self in a function that uses :. Could someone please explain to me why this happens?
The notation A:update(x) is syntactic sugar for A.update(A,x). That means you have to call the function update with two parameters. You are lacking the first of the two parameters.
The first parameter A is already on the stack but is located “below” the update function. Using lua_pushvalue we can push a copy of the table onto the stack.
Thus you have to call the function like this (omitting the error handling bits)
lua_getfield(L, -1, "update");
lua_pushvalue(L, -2); // push a copy of "A" onto the stack
lua_pushnumber(L, 5); //pass the argument 5
lua_pcall(L, 2, LUA_MULTRET, 0);

Transfer multidimensional lua array from lua_State to lua_State C++

I have 2 scripts, each are in a different lua_State.
I am trying to be able to get a variable from one state and use it in the other.
My code below works for single variables and unidirectional arrays. Could I get some guidance on also making it work for multidimensional arrays?
void getValues(lua_State* L1, lua_State* L2, int& returns)
{
if (lua_isuserdata(L1, -1))
{
LuaElement* e = Luna<LuaElement>::to_object(L1, -1);
if (e != NULL)
{
Luna<LuaElement>::push_object(L2, e);
}
}
else if (lua_isstring(L1, -1))
{
lua_pushstring(L2, lua_tostring(L1, -1));
}
else if (lua_isnumber(L1, -1))
lua_pushnumber(L2, lua_tonumber(L1, -1));
else if (lua_isboolean(L1, -1))
lua_pushboolean(L2, lua_toboolean(L1, -1));
else if (lua_istable(L1, -1))
{
lua_pushnil(L1);
lua_newtable(L2);
while (lua_next(L1, -2))
{
getValues(L1, L2, returns);
lua_rawseti(L2,-2,returns-1);
lua_pop(L1, 1);
}
// lua_rawseti(L2,-2,returns); // this needs work
}
returns++;
}
Unfortunately I'm having a tough time getting the recursion right for this to work for multidimensional arrays.
Solved.
For anyone this might be useful:
void getValues(lua_State* L1, lua_State* L2, int ind)
{
if (lua_type(L1, -1) == LUA_TTABLE)
{
lua_newtable(L2);
lua_pushnil(L1);
ind = 0;
while (lua_next(L1, -2))
{
// push the key
if (lua_type(L1, -2) == LUA_TSTRING)
lua_pushstring(L2, lua_tostring(L1, -2));
else if (lua_type(L1, -2) == LUA_TNUMBER)
lua_pushnumber(L2, lua_tonumber(L1, -2));
else
lua_pushnumber(L2, ind);
getValues(L1, L2, ind++);
lua_pop(L1, 1);
lua_settable(L2, -3);
}
}
else if (lua_type(L1, -1) == LUA_TSTRING)
{
lua_pushstring(L2, lua_tostring(L1, -1));
}
else if (lua_type(L1, -1) == LUA_TNUMBER)
{
lua_pushnumber(L2, lua_tonumber(L1, -1));
}
else if (lua_type(L1, -1) == LUA_TBOOLEAN)
{
lua_pushboolean(L2, lua_toboolean(L1, -1));
}
else if (lua_type(L1, -1) == LUA_TUSERDATA)
{
// replace with your own user data. This is mine
LuaElement* e = Luna<LuaElement>::to_object(L1, -1);
if (e != NULL)
{
Luna<LuaElement>::push_object(L2, e);
}
}
}
Warning: L1 and L2 must be different states.
You can try lua_tinker::table:

Lua return custom data from C function

Despite searching hard, i couldn't find a valid Lua C API example for calling a Lua function returning custom data from C function.
For example, I have register function "GetMyVector" and then I'm calling that from lua to retrive informations from C, what I got is a table but what I want is something like access to access variable from struct like in C, for example:
local x = GetMyVector()
print(x[1]) -- i
print(x[2]) -- j
print(x[3]) -- k
-- how to access it via like this:
print(x.i)
print(x.j)
print(x.k)
my C function pushing vector in 3 dimensional array by lua_pushnumber:
static int GetMyVector(lua_State *L)
{
vec3_t vec;
vec[0] = 1;
vec[1] = 2;
vec[3] = 3;
lua_newtable(L);
lua_pushnumber(L, vec[0]);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, vec[1]);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, vec[2]);
lua_rawseti(L, -2, 3);
return 1;
}
You probably want lua_settable. It allows to set any keys for the table. If the key is literal you can get data by x["i"] or by x.i .
The code should be something like
static int GetMyVector(lua_State *L)
{
vec3_t vec;
vec[0] = 1;
vec[1] = 2;
vec[3] = 3;
lua_newtable(L);
lua_pushliteral(L, "i");
lua_pushnumber(L, vec[0]);
lua_settable(L, -2);
lua_pushliteral(L, "j");
lua_pushnumber(L, vec[1]);
lua_settable(L, -2);
lua_pushliteral(L, "k");
lua_pushnumber(L, vec[2]);
lua_settable(L, -2);
return 1;
}
It's a little bit extended, but thanks to #geov I have found what I was looking for, it's kinda similar to properties style like in C# i think, here you go the solution:
#define MYCLASSNAME "vec"
typedef struct {
int i, j, k;
} MyVec_t;
typedef int(*Xet_func) (lua_State *L, void *v);
/* member info for get and set handlers */
typedef const struct{
const char *name; /* member name */
Xet_func func; /* get or set function for type of member */
size_t offset; /* offset of member within MyVec_t */
} Xet_reg_pre;
typedef Xet_reg_pre * Xet_reg;
// properties
static int Get_Int(lua_State *L, void *v) {
lua_pushnumber(L, *(int*)v);
return 1;
}
static int Set_Int(lua_State *L, void *v) {
*(int*)v = luaL_checkinteger(L, 3);
return 0;
}
static int Get_Number(lua_State *L, void *v) {
lua_pushnumber(L, *(lua_Number*)v);
return 1;
}
static int Set_Number(lua_State *L, void *v) {
*(lua_Number*)v = luaL_checknumber(L, 3);
return 0;
}
static int Get_String(lua_State *L, void *v) {
lua_pushstring(L, (char*)v);
return 1;
}
static void Property_Add(lua_State *L, Xet_reg l)
{
for (; l->name; l++) {
lua_pushstring(L, l->name);
lua_pushlightuserdata(L, (void*)l);
lua_settable(L, -3);
}
}
static int Property_Call(lua_State *L)
{
Xet_reg m = (Xet_reg)lua_touserdata(L, -1);
lua_pop(L, 1);
luaL_checktype(L, 1, LUA_TUSERDATA);
return m->func(L, (void *)((char *)lua_touserdata(L, 1) + m->offset));
}
static int index_handler(lua_State *L)
{
/* stack has userdata, index */
lua_pushvalue(L, 2); /* dup index */
lua_rawget(L, lua_upvalueindex(1)); /* lookup member by name */
if (!lua_islightuserdata(L, -1)) {
lua_pop(L, 1); /* drop value */
lua_pushvalue(L, 2); /* dup index */
lua_gettable(L, lua_upvalueindex(2)); /* else try methods */
if (lua_isnil(L, -1)) /* invalid member */
luaL_error(L, "cannot get member '%s'", lua_tostring(L, 2));
return 1;
}
return Property_Call(L); /* call get function */
}
static int newindex_handler(lua_State *L)
{
/* stack has userdata, index, value */
lua_pushvalue(L, 2); /* dup index */
lua_rawget(L, lua_upvalueindex(1)); /* lookup member by name */
if (!lua_islightuserdata(L, -1)) /* invalid member */
luaL_error(L, "cannot set member '%s'", lua_tostring(L, 2));
return Property_Call(L); /* call set function */
}
static MyVec_t *CheckMyVec(lua_State *L, int index) // get data
{
MyVec_t *p;
luaL_checktype(L, index, LUA_TUSERDATA);
p = (MyVec_t *)luaL_checkudata(L, index, MYCLASSNAME);
return p;
}
static MyVec_t *PushMyVec(lua_State *L) // push data
{
MyVec_t *p = (MyVec_t *)lua_newuserdata(L, sizeof(MyVec_t));
luaL_getmetatable(L, MYCLASSNAME);
lua_setmetatable(L, -2);
return p;
}
static int MyVec_Create(lua_State *L) // C function which will push data
{
MyVec_t *p;
p = PushMyVec(L);
p->i = luaL_checkinteger(L, 1);
p->j = luaL_checkinteger(L, 2);;
p->k = luaL_checkinteger(L, 3);;
return 1;
}
static int MyVec_destroy(lua_State *L)
{
MyVec_t *p = (MyVec_t *)lua_touserdata(L, 1);
return 0;
}
static int MyVec_Position(lua_State *L)
{
MyVec_t *p = CheckMyVec(L, 1);
double x = p->i;
double y = p->j;
double z = p->k;
if (lua_gettop(L) > 1) {
p->i = luaL_checknumber(L, 2);
p->j = luaL_checknumber(L, 3);
p->k = luaL_checknumber(L, 4);
}
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 2;
}
static const luaL_Reg myvec_meta_methods[] = {
{ "__gc", MyVec_destroy },
{ 0, 0 }
};
static const luaL_Reg myvec_methods[] = {
{ "create", MyVec_Create },
{ "position", MyVec_Position },
{ 0, 0 }
};
static const Xet_reg_pre MyVec_get[] = {
{ "i", Get_Int, offsetof(MyVec_t, i) },
{ "j", Get_Int, offsetof(MyVec_t, j) },
{ "k", Get_Int, offsetof(MyVec_t, k) },
{ 0, 0 }
};
static const Xet_reg_pre MyVec_set[] = {
{ "i", Set_Int, offsetof(MyVec_t, i) },
{ "j", Set_Int, offsetof(MyVec_t, j) },
{ "k", Set_Int, offsetof(MyVec_t, k) },
{ 0, 0 }
};
int MyVec_Register(lua_State *L)
{
int metatable, methods;
/* create methods table, & add it to the table of globals */
luaL_openlib(L, MYCLASSNAME, myvec_methods, 0);
methods = lua_gettop(L);
/* create metatable for MyVec_t, & add it to the registry */
luaL_newmetatable(L, MYCLASSNAME);
luaL_openlib(L, 0, myvec_meta_methods, 0); /* fill metatable */
metatable = lua_gettop(L);
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methods); /* dup methods table*/
lua_rawset(L, metatable); /* hide metatable:
metatable.__metatable = methods */
lua_pushliteral(L, "__index");
lua_pushvalue(L, metatable); /* upvalue index 1 */
Property_Add(L, MyVec_get); /* fill metatable with getters */
lua_pushvalue(L, methods); /* upvalue index 2 */
lua_pushcclosure(L, index_handler, 2);
lua_rawset(L, metatable); /* metatable.__index = index_handler */
lua_pushliteral(L, "__newindex");
lua_newtable(L); /* table for members you can set */
Property_Add(L, MyVec_set); /* fill with setters */
lua_pushcclosure(L, newindex_handler, 1);
lua_rawset(L, metatable); /* metatable.__newindex = newindex_handler */
lua_pop(L, 1); /* drop metatable */
return 1; /* return methods on the stack */
}

Why is Lua returning an empty table in this code?

edit:
As I commented before, my problem is I can't create a table in C which uses strings as keys. I made a quick test program which demonstrates the problem I'm having:
Here is the C++ part of it:
#include <lua.hpp>
#include <String.h>
BString
LuaTypeToString(lua_State *L, int index, int type)
{
BString out;
switch (type)
{
case LUA_TSTRING:
{
out << "'" << lua_tostring(L, index) << "'";
break;
}
case LUA_TBOOLEAN:
{
out << (lua_toboolean(L, index) ? "true" : "false");
break;
}
case LUA_TNUMBER:
{
out << (float)lua_tonumber(L, index);
break;
}
default:
{
out << lua_typename(L, type);
break;
}
}
return out;
}
void
DumpLuaTable(lua_State *L, int tableIndex)
{
lua_pushnil(L);
printf("\t{ ");
while (lua_next(L, tableIndex) != 0)
{
BString keyString = lua_tostring(L, -2);
BString valueString;
int type = lua_type(L, -1);
if (type == LUA_TTABLE)
DumpLuaTable(L, lua_gettop(L));
else
valueString = LuaTypeToString(L, -1, type);
printf("%s=%s,",
keyString.String(),
valueString.String());
lua_pop(L, 1);
if (lua_isnumber(L, -1))
{
lua_pop(L, 1);
break;
}
}
printf(" }");
}
void
DumpLuaStack(lua_State *L)
{
printf("DumpLuaStack:\n");
int top = lua_gettop(L);
for (int i = 1; i <= top; i++)
{
int type = lua_type(L, i);
if (type == LUA_TTABLE)
DumpLuaTable(L, i);
else
printf("\t%s ", LuaTypeToString(L, i, type).String());
}
printf("\n");
}
static
int
ReturnTable(lua_State *L)
{
lua_newtable(L);
lua_pushnumber(L, 3.14);
lua_pushstring(L, "1");
lua_settable(L, -3);
DumpLuaStack(L);
return 1;
}
int
main(void)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_pushcfunction(L, ReturnTable);
lua_setglobal(L, "ReturnTable");
int status = luaL_dofile(L, "luatest.lua");
if (status)
printf("Status = %d: %s\n", status, lua_tostring(L, -1));
lua_close(L);
return status;
}
And the Lua part of it:
function DumpTable(table)
print("LuaDumpTable")
local out = "\t"
if #table > 0 then
for i, v in ipairs(table) do
out = out .. i .. "=" .. v .. " "
end
print(out)
else
print("\tEmpty table")
end
end
value = ReturnTable()
if (type(value) == "table") then
DumpTable(value)
else
print(type(value))
end
The code as it is works without problem -- the Lua script prints out what you would expect. Change the line which pushes 3.14 to the stack to, say, lua_pushstring(L, "foo"); and all I get is an empty table on the Lua side. Any ideas what I'm doing wrong here?
You're checking the table incorrectly in Lua:
if #table > 0 then
The #table is just telling you that the first unused integer in the table is 0. When you have a string inserted into the table, it doesn't increment the count since it's not part of a continuous array, and you also can't process it with ipairs. Just switch to using pairs and you should see foo:
function DumpTable(table)
print("LuaDumpTable")
local out = "\t"
for i, v in pairs(table) do
out = out .. i .. "=" .. v .. " "
end
print(out)
end
Older note, left as an fyi:
I doubt this would cause the issue, but for API's being called from Lua, you typically want to pop the input off of the stack. So after the lua_tonumber call:
int32 value = lua_tonumber(L, 1);
lua_pop(L, 1);
rgb_color c = ui_color((color_which)value);