Lua return custom data from C function - c++

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 */
}

Related

Modifying a C++ array in main() from Lua without extra allocation

I am sketching a small C++ program that will pass arrays to Lua and have them modified there, where I intend to have a lua script read in the program so I can modify it without needing to recompile the program
My first obstacle is to ensure Lua is able to modify arrays already allocated instead of having them allocated again in the Lua space. The data will be float and the size will be really large, but I am starting small for the moment.
To simplify this interface I tried LuaBridge 2.6, but it doesn't provide the expected result. Below is a fully "working" program.
#include <iostream>
#include <cstdint>
#include <cstring>
#include <vector>
#include <lua5.3/lua.hpp>
#include <LuaBridge/LuaBridge.h>
int main(void)
{
const uint32_t LENGTH = 512 * 256;
std::vector <float> input(LENGTH),
output(LENGTH);
memset(output.data(), 0, LENGTH * sizeof(float)); // Zero the output
for(uint32_t i = 0; i < LENGTH; i++) // Populate input
input[i] = (float)i + 0.5f;
lua_State *luastate = luaL_newstate();
luabridge::push(luastate, input.data()); // Supposedly passing a pointer to the first element of input, according to LuaBridge manual chap 3-3.1
luabridge::push(luastate, output.data()); // Same for output
luaL_dostring(luastate, "output[10] = input[256]"); // Expecting to assign this value in the C++ arrays, not in the Lua space
lua_getglobal(luastate, "output[10]"); // Find this assigned value in the Lua stack
lua_Number val = lua_tonumber(luastate, 1); // Retrieving this value from Lua to C++
std::cout << input[256] << ", " << output[10] << ", " << val << std::endl; // The values of val and in output[10] don't match
lua_close(luastate);
return 0;
}
Notice that nothing matches. What is going to output[10] in Lua is not the value of input[256] in the C++ space, but input[0].
The C++ output array is not updated from within Lua, cout shows that it remains as we initialized (0).
To confirm that, we pushed this value of output[10] to the stack, which is not input[256] in C++, and retrieved from C++.
Can you guys correct me or point me to where I should be going to achieve this?
======= UPDATE 08/11/2020 =======
To clarify what the program is doing (or supposed to do), after reading Robert's and Joseph's considerations, I post below an updated version of both the C++ part and the lua script called by it. Notice I abandoned LuaBridge since I didn't succeed in the first attempt:
C++:
#include <iostream>
#include <cstdint>
#include <cstring>
#include <vector>
#include <luajit-2.0/lua.hpp> // LuaJIT 2.0.4 from Ubuntu 16.04
int main(void)
{
const uint32_t LENGTH = 256 * 512;
std::vector <float> input(LENGTH),
output(LENGTH);
memset(output.data(), 0, LENGTH * sizeof(float));
for(uint32_t i = 0; i < LENGTH; i++)
input[i] = (float)i + 0.5f;
lua_State *luastate = luaL_newstate();
luaL_openlibs(luastate);
// Here I have to pass &input[0], &output[0] and LENGTH
// to Lua, which in turn will pass to whatever functions
// are being called from a .so lib opened in Lua-side
luaL_dofile(luastate, "my_script.lua");
lua_close(luastate);
return 0;
}
The Lua script looks like this:
local ffi = require("ffi")
local mylib = ffi.load("/path_to_lib/mylib.so")
-- Here I import and call any fuctions needed from mylib.so
-- without needing to recompile anything, just change this script
-- At this point the script has to know &input[0], &output[0] and LENGTH
ffi.cdef[[int func1(const float *in, float *out, const uint32_t LEN);]]
ffi.cdef[[int func2(const float *in, float *out, const uint32_t LEN);]]
ffi.cdef[[int funcX(const float *in, float *out, const uint32_t LEN);]]
if(mylib.func1(input, output, LENGTH) == 0) then
print("Func1 ran successfuly.")
else
print("Func1 failed.")
end
I am sketching a small C++ program that will pass arrays to Lua
The data will be float and the size will be really large,
My suggestion:
Keep the buffer on the C side (as a global variable for example)
Expose a C-function to LUA GetTableValue(Index)
Expose a C-function to Lua SetTableValue(Index, Value)
It should be something like this:
static int LUA_GetTableValue (lua_State *LuaState)
{
float Value;
/* lua_gettop returns the number of arguments */
if ((lua_gettop(LuaState) == 1) && (lua_isinteger(LuaState, -1)))
{
/* Get event string to execute (first parameter) */
Offset = lua_tointeger(LuaState, -1);
/* Get table value */
Value = LUA_FloatTable[Offset];
/* Push result to the stack */
lua_pushnumber(Value);
}
else
{
lua_pushnil(LuaState);
}
/* return 1 value */
return 1;
}
And you also need to register the function:
lua_register(LuaState, "GetTableValue", LUA_GetTableValue);
I let you write the SetTableValue but it should be very close.
Doing so, the buffer is on C side and can be accessed from Lua with dedicated functions.
I recommend you create a userdata that exposes the arrays via __index and __newindex, something like this (written as a C and C++ polyglot like Lua itself):
#include <stdio.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <lua5.3/lua.h>
#include <lua5.3/lauxlib.h>
#ifdef __cplusplus
}
#endif
struct MyNumbers {
lua_Number *arr;
lua_Integer len;
};
int MyNumbers_index(lua_State *L) {
struct MyNumbers *t = (struct MyNumbers *)luaL_checkudata(L, 1, "MyNumbers");
lua_Integer k = luaL_checkinteger(L, 2);
if(k >= 0 && k < t->len) {
lua_pushnumber(L, t->arr[k]);
} else {
lua_pushnil(L);
}
return 1;
}
int MyNumbers_newindex(lua_State *L) {
struct MyNumbers *t = (struct MyNumbers *)luaL_checkudata(L, 1, "MyNumbers");
lua_Integer k = luaL_checkinteger(L, 2);
if(k >= 0 && k < t->len) {
t->arr[k] = luaL_checknumber(L, 3);
return 0;
} else {
return luaL_argerror(L, 2,
lua_pushfstring(L, "index %d out of range", k));
}
}
struct MyNumbers *MyNumbers_new(lua_State *L, lua_Number *arr, lua_Integer len) {
struct MyNumbers *var = (struct MyNumbers *)lua_newuserdata(L, sizeof *var);
var->arr = arr;
var->len = len;
luaL_setmetatable(L, "MyNumbers");
return var;
}
int main(void) {
const lua_Integer LENGTH = 512 * 256;
lua_Number input[LENGTH], output[LENGTH];
memset(output, 0, sizeof output);
for(lua_Integer i = 0; i < LENGTH; ++i)
input[i] = i + 0.5f;
lua_State *L = luaL_newstate();
luaL_newmetatable(L, "MyNumbers");
lua_pushcfunction(L, MyNumbers_index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, MyNumbers_newindex);
lua_setfield(L, -2, "__newindex");
/* exercise for the reader: implement __len and __pairs too, and maybe shift the indices so they're 1-based to Lua */
lua_pop(L, 1);
MyNumbers_new(L, input, LENGTH);
lua_setglobal(L, "input");
MyNumbers_new(L, output, LENGTH);
lua_setglobal(L, "output");
luaL_dostring(L, "output[10] = input[256]");
lua_getglobal(L, "output");
lua_geti(L, -1, 10);
lua_Number val = lua_tonumber(L, -1);
printf("%f, %f, %f\n", input[256], output[10], val);
lua_close(L);
}
With this approach, there is no copy of any data in Lua, and your own MyNumbers_ functions control how all access to them is done.
If you want to be able to use the arrays through LuaJIT's FFI instead of directly manipulating them in Lua, then you can pass their addresses in a light userdata instead, like this:
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <luajit-2.0/lua.h>
#include <luajit-2.0/lualib.h>
#include <luajit-2.0/lauxlib.h>
#ifdef __cplusplus
}
#endif
int main(void) {
const lua_Integer LENGTH = 256 * 512;
lua_Number input[LENGTH], output[LENGTH];
memset(output, 0, sizeof output);
for(lua_Integer i = 0; i < LENGTH; ++i)
input[i] = i + 0.5f;
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_pushlightuserdata(L, input);
lua_setglobal(L, "input");
lua_pushlightuserdata(L, output);
lua_setglobal(L, "output");
lua_pushinteger(L, LENGTH);
lua_setglobal(L, "LENGTH");
luaL_dofile(L, "my_script.lua");
lua_close(L);
}

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);
}

Lua "attempt to index a nil value"

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.

Extract userdata from table,using lua c API

I use lua c api to loop the variables in a table, like this
lua script:
array = {0,1,2,3}
lua c api
lua_getglobal(l, "array");
if(lua_isnil(l, -1)) {
}
lua_pushnil(l);
while(lua_next(l, -2)) {
int value=(int)lua_tonumber(l, -1);
printf("%d \n",value);
lua_pop(l, 1);
}
and I can get the result
0
1
2
3
Then I want to put some userdata objs into the table
and then loop them in the c api
lua script
foo0 = Foo.new("fred0")
foo1 = Foo.new("fred0")
foo2 = Foo.new("fred0")
foo3 = Foo.new("fred0")
array = {foo0,foo1,foo2,foo3}
lua c api
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include <iostream>
#include <sstream>
#include <vector>
class Foo
{
public:
Foo(const std::string & name) : name(name)
{
std::cout << "Foo is born" << std::endl;
}
std::string Add(int a, int b)
{
std::stringstream ss;
ss << name << ": " << a << " + " << b << " = " << (a+b);
return ss.str();
}
~Foo()
{
std::cout << "Foo is gone" << std::endl;
}
std::string name;
};
int l_Foo_constructor(lua_State * l)
{
const char * name = luaL_checkstring(l, 1);
Foo ** udata = (Foo **)lua_newuserdata(l, sizeof(Foo *));
*udata = new Foo(name);
luaL_getmetatable(l, "luaL_Foo");
lua_setmetatable(l, -2);
return 1;
}
Foo * l_CheckFoo(lua_State * l, int n)
{
return *(Foo **)luaL_checkudata(l, n, "luaL_Foo");
}
int l_Foo_add(lua_State * l)
{
return 1;
}
int l_Foo_destructor(lua_State * l)
{
Foo * foo = l_CheckFoo(l, 1);
delete foo;
return 0;
}
void RegisterFoo(lua_State * l)
{
luaL_Reg sFooRegs[] =
{
{ "new", l_Foo_constructor },
{ "add", l_Foo_add },
{ "__gc", l_Foo_destructor },
{ NULL, NULL }
};
luaL_newmetatable(l, "luaL_Foo");
luaL_register(l, NULL, sFooRegs);
lua_pushvalue(l, -1);
lua_setfield(l, -1, "__index");
lua_setglobal(l, "Foo");
}
int main()
{
lua_State * l = luaL_newstate();
luaL_openlibs(l);
RegisterFoo(l);
int erred = luaL_dofile(l,"/Volumes/Work/CODE/Test/testStatic/testStatic/kami.txt");
if(erred)
std::cout << "Lua error: " << luaL_checkstring(l, -1) << std::endl;
lua_getglobal(l, "array");
if(lua_isnil(l, -1)) {
//return std::vector();
}
lua_pushnil(l);
std::vector<Foo *> v;
while(lua_next(l, -2)) {
Foo * foo = l_CheckFoo(l, -1);//this line do not work
//
//
//
//
//I don't know how to do it here.
//
//
//
//
//v.push_back(foo);
lua_pop(l, 1);
}
// for (Foo* theValue:v)
// {
// printf("==>%s",theValue->name.c_str());
// }
lua_close(l);
return 0;
}
how to extract userdata from a table?
please help me,thank you.
finally I soved it by myself
l_CheckFoo(l, -1);
should be
lua_touserdata(l, -1);
void * hehe=lua_touserdata(l, -1);
Foo ** haha=(Foo **)hehe;
Foo *f=*haha;
printf("%s \n",f->name.c_str());

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