im trying to call a lua function from C++ where function is in subtable of global table. Im using lua version 5.2.* compiled from source.
Lua function
function globaltable.subtable.hello()
-- do stuff here
end
C++ code
lua_getglobal(L, "globaltable");
lua_getfield(L, -1, "subtable");
lua_getfield(L, -1, "hello");
if(!lua_isfunction(L,-1)) return;
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_call(L, 2, 0);
However im unable to call it, i always get an error
PANIC: unprotected error in call to Lua API (attempt to index a nil value)
On line #3: lua_getfield(L, -1, "hello");
What am I missing?
Side question: I would love to know also how to call function deeper than this - like globaltable.subtable.subsubtable.hello() etc.
Thank you!
This is what im using to create the globaltable:
int lib_id;
lua_createtable(L, 0, 0);
lib_id = lua_gettop(L);
luaL_newmetatable(L, "globaltable");
lua_setmetatable(L, lib_id);
lua_setglobal(L, "globaltable");
how do i create the globaltable.subtable?
function is a keyword in Lua, I am guessing on how did you manage to compile the code:
-- test.lua
globaltable = { subtable = {} }
function globaltable.subtable.function()
end
When this is run:
$ lua test.lua
lua: test.lua:2: '<name>' expected near 'function'
Maybe you changed the identifiers for this online presentation, but check that on line 2 "subtable" really exists in the globaltable, because on line 3, the top of stack is already nil.
Update:
To create multiple levels of tables, you can use this approach:
lua_createtable(L,0,0); // the globaltable
lua_createtable(L,0,0); // the subtable
lua_pushcfunction(L, somefunction);
lua_setfield(L, -2, "somefunction"); // set subtable.somefunction
lua_setfield(L, -2, "subtable"); // set globaltable.subtable
lua_newtable(L);
luaL_newmetatable(L, "globaltable");
lua_newtable(L); //Create table
lua_setfield(L, -2, "subtable"); //Set table as field of "globaltable"
lua_setglobal(L, "globaltable");
This is what i was looking for.
Related
Nothing I do seems to work and I've tried everything known to mankind at this point.
Here is C code:
int l_getHeader(lua_State* L)
{
lua_createtable(L, 0, 5);
lua_pushnumber(L, asset);
lua_setfield(L, -2, "asset");
lua_pushnumber(L, partner);
lua_setfield(L, -2, "partner");
lua_pushnumber(L, collector);
lua_setfield(L, -2, "collector");
lua_pushnumber(L, tm);
lua_setfield(L, -2, "time");
lua_pushnumber(L, count);
lua_setfield(L, -2, "count");
lua_pushnil(L);
lua_setglobal(L, "header");
return 1
and here is the lua call
function load_header()
local header = {}
-- still not sure if should assign return
get_header()
-- header = get_header()
print("asset " .. header["asset"])
print("partner " .. header["partner"])
print("collector " .. header["collector"])
print("time " .. header["time"])
print("count " .. header["count"])
end
I always get the same error. I've tried pushing all items onto the stack, rawset, etc. NADA!! This is the simplest example I could come up with that also doesn't work and seemingly should.
I am trying to index into a user data called entity to get to another user data called transform component. I am relatively new to lua w/ c++ binding but after I got my entity up and running with metatables and an index method that would allow me to do stuff like this in Lua:
entity1 = Entity.create()
entity1:setID(4)
print(entity1)
This works perfectly but I also have a user data called TransformComponent that looks like this in LUA
transform1 = TransformComponent.create()
transform1:setPosition(4,3)
print(transform1)
Here is where I get stuck. What I WANT to be able to do in LUA is something like this:
entity1 = Entity.create()
entity1.transform:setPosition(4,3)
print(entity1)
How do I (in C++) set up this kind of relationship where Entity is a table that contains all its methods but also contains a key called "transform" that maps to the TransformComponent table. In C++ I tried setting it as a key and I tried reading the "transform" key from the index method and placing the TransformComponent methods in the table but that didn't work.
Any help is extremely appreciated.
EDIT
I initially didn't want to include the attempts I made because they were futile and I knew it but what I attempted to do was during the entity index, I would push the table for TransformComponent and hope the next part of the index (i.e. the methods for TransformComponent)
So here is the index method for Entity
static int _LUA_index(lua_State* L) {
assert(lua_isstring(L, -1));
Entity* luaEntity1 = (Entity*)lua_touserdata(L, -2); assert(luaEntity1);
std::string index = lua_tostring(L, -1);
if (index == "transform") {
lua_getglobal(L, "TransformComponent");
return 1;
}
lua_getglobal(L, "Entity");
lua_pushstring(L, index.c_str());
lua_rawget(L, -2);
return 1;
}
If i print out the result of "index" its just transform, I can never get it to try and parse "setPosition". I have no Idea how to progress here. Here is the code to setup the tables:
lua_State* L = luaL_newstate();
luaL_openlibs(L);
/* --------- Transform table ----------- */
lua_newtable(L);
int transformTableIndex = lua_gettop(L);
lua_pushvalue(L, transformTableIndex);
lua_setglobal(L, "TransformComponent");
lua_pushcfunction(L, TransformComponent::_LUA_CreateTransform);
lua_setfield(L, -2, "create");
lua_pushcfunction(L, TransformComponent::_LUA_SetPosition);
lua_setfield(L, -2, "setPosition");
/* --------- Transform Meta table ----------- */
luaL_newmetatable(L, "TransformComponentMetaTable");
lua_pushcfunction(L, TransformComponent::_LUA_gc);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, TransformComponent::_LUA_eq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, TransformComponent::_LUA_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, TransformComponent::_LUA_index);
lua_setfield(L, -2, "__index");
/* --------- Entity table --------- */
lua_newtable(L);
int entityTableIndex = lua_gettop(L);
lua_pushvalue(L, entityTableIndex);
lua_setglobal(L, "Entity");
constexpr int NUM_OF_UPVALUES = 1;
lua_pushlightuserdata(L, &manager);
lua_pushcclosure(L, Entity::_LUA_CreateEntity, NUM_OF_UPVALUES);
//lua_pushcfunction(L, Entity::_LUA_CreateEntity);
lua_setfield(L, -2, "create");
lua_pushcfunction(L, Entity::_LUA_MoveEntity);
lua_setfield(L, -2, "move");
lua_pushcfunction(L, Entity::_LUA_DrawEntity);
lua_setfield(L, -2, "draw");
/* --------- Entity Meta table --------- */
luaL_newmetatable(L, "EntityMetaTable");
lua_pushstring(L, "__gc");
lua_pushcfunction(L, Entity::_LUA_gc);
lua_settable(L, -3);
lua_pushstring(L, "__eq");
lua_pushcfunction(L, Entity::_LUA_eq);
lua_settable(L, -3);
lua_pushstring(L, "__tostring");
lua_pushcfunction(L, Entity::_LUA_tostring);
lua_settable(L, -3);
lua_pushstring(L, "__index");
lua_pushcfunction(L, Entity::_LUA_index);
lua_settable(L, -3);
I started thinking this was impossible to accomplish or I am just unable to do this Lua. I am unsure but I am definitely not talented enough to figure this out.
EDIT2
EntityClass + TransformComponent:
struct _API SpriteComponent {
const char* fileName;
};
struct _API TransformComponent {
glm::vec2 position;
glm::vec3 rotation;
TransformComponent();
~TransformComponent();
static int _LUA_CreateTransform(lua_State* L);
static int _LUA_SetPosition(lua_State* L);
static int _LUA_gc(lua_State* L);
static int _LUA_eq(lua_State* L);
static int _LUA_index(lua_State* L);
static int _LUA_tostring(lua_State* L);
};
class _API Entity{
public:
TransformComponent transform;
SpriteComponent sprite;
Entity();
~Entity();
void moveEntity(float x, float y);
void drawEntity();
static int _LUA_CreateEntity(lua_State* L);
..etc etc
EDIT3
_LUA_CreateTransform method
int TransformComponent::_LUA_CreateTransform(lua_State* L) {
void* entityPointer = lua_newuserdata(L, sizeof(TransformComponent));
new(entityPointer) TransformComponent(); assert(entityPointer);
luaL_getmetatable(L, "TransformComponentMetaTable");
lua_setmetatable(L, -2);
return 1;
}
Again any help is super appreciated.
Your _LUA_index C++ function is more-or-less equivalent to this in pure Lua:
function EntityMetaTable.__index(luaEntity1, index)
if tostring(index) == 'transform' then
return TransformComponent
end
return rawget(Entity, index)
end
Thus, when you do entity1.transform:setPosition(4,3), it's basically equivalent to TransformComponent:setPosition(4,3). The problem is that you can't do setPosition directly on the TransformComponent table. You need to do it on a userdata that TransformComponent.create() gives you, like transform1 is, instead. If you want to avoid making a fresh TransformComponent userdata every time, you can save the result as a uservalue (or environment if you're on Lua 5.1 still) on your entity userdata.
Edit: Now that I see more of your code, here's what you should do. In your Entity class, change TransformComponent transform to TransformComponent *transform. Then, in your _LUA_CreateEntity function, set up transform the same way that you do in _LUA_CreateTransform. Then set the transform userdata as the environment or uservalue (depending on Lua version) of the entity userdata. Finally, in your index method, return that environment/uservalue instead of doing lua_getglobal(L, "TransformComponent");.
You are returning a static class/table (TransformComponent in this case) when your entity's index transform is called. What you really need is the actual instance of your transform that is present inside your entity class. So technically, you want properties of your object to be accessible from Lua easily by calling ent.transform.
To achieve this, you can use sol which is "a fast, simple C++ and Lua Binding".
class _API Entity{
public:
TransformComponent transform;
TransformComponent get_transform() {
return transform;
}
void set_transform(TransformComponent value) {
transform = value;
}
...
#include <sol/sol.hpp>
int main (int, char*[]) {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<Entity>( "Entity",
"transform", sol::property(&Entity::get_transform, &Entity::set_transform)
);
Additionally, you can have properties that are read-only which would fit your transform property perfectly since you do not want it to be replaced with a new transform and rather you want the transform properties to be changed.
sol::usertype<Entity> type_ent = lua.new_usertype<Entity>("Entity",
sol::constructors<Entity()>());
// Read only property (Still, you can call your Transform methods on it).
type_ent.set("transform", sol::readonly(&Entity::get_transform));
As a side note, be sure to first register your TransformComponent with all it's functions and properties and then register your Entity.
Have a look at sol properties and sol classes instruction for more details.
I have the following simple code in C++ where Object is a std container:
static int create_an_object(lua_State* L) {
auto obj = static_cast<Object*>(lua_newuserdata(L, sizeof(Object*)));
*obj = another_valid_obj;
luaL_newmetatable(L, "object_metatable");
lua_pushcfunction(L, object_metatable_function);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
return 1;
}
static int object_metatable_function(lua_State* L) {
string index = luaL_checkstring(L, -1);
if (index == "foo") {
lua_pushnumber(L, 123);
}
// Handles other indices, or throws error.
}
lua_pushcfunction(L, create_an_object);
lua_setglobal(L, "create_an_object");
With the FFI above, I can achieve indexing of Object in Lua such as:
local obj = create_an_object()
print(obj.foo) -- 123
Meanwhile print(obj) shows that obj is userdata: 0x12345678.
Is it possible to use some metamethod magic so that
obj could be used as a table, while print(obj.foo) still prints 123? I am running my code in Lua 5.1.
I'm not exactly sure what you mean by "could be used as a table", but if you want to print something different from the default from print(obj), then you'll need to assign __tostring metamethod and return some string from it. This string may look like "userdata: 0x12345678 = {foo = 123}" if you want (or simply "{foo = 123}").
If you mean making it work as a table when assigning a new index to it, then __newindex metamethod should be used.
I need an idea, how I can store lua closures to invoke them asynchronously later.
my first idea was lua_tocfunction but a closure is not a cfunction and cannot be invoked from C directly
second idea was to save the closure in the metatable, that I can push it and call it later, but it seems, that I cannot copy a closure. (Error: attempt to index a function value).
So I need your help please. How can I store a closure?
I admit, that I did not completely understand why there is an __index field in my lua ctor as I've copied that part from somewhere.
By the way: the program without onrender worked as expected. I'm using qt gui and the lua-states are closed, after qt's main loop, thus the created window is not going to be delete by __gc after the script.
bootstrap.lua
local w = w_render() -- create window object
w:show()
w:onrender(function()
print('render')
end)
w_lua.cpp
// chlua_* are helper macros/templates/methods
// 1: self
// 2: render closure
int w_render_onrender(lua_State *L) {
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
lua_pushvalue(L, 2); // copy closure to top
lua_setfield(L, 2, "onrender_cb"); // save closure in metatable
// !!! ERROR: attempt to index a function value
self->onrender([L](){
lua_getfield(L, 2, "onrender_cb");
qDebug() << "onrender";
lua_call(L, 0, 0);
});
return 0;
}
// Creates the object
int w_render(lua_State *L) {
auto *&self = chlua_newuserdata<GLWindow *>(L);
self = new GLWindow;
if (luaL_newmetatable(L, w_render_table)) {
luaL_setfuncs(L, w_render_methods, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
}
lua_setmetatable(L, -2);
return 1;
}
It looks like your problem is stemming from using the wrong indices and attempting to set/get fields on the wrong lua object on the stack. Assuming the udata representing your GLWindow * is first followed by the lua closure second, try changing the code like this:
int w_render_onrender(lua_State *L)
{
luaL_checkudata(L, 1, w_render_table);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
lua_getmetatable(L, 1);
lua_insert(L, -2); // GLWindow GLWindow_mt lua_closure
lua_setfield(L, -2, "onrender_cb"); // save closure in metatable
self->onrender([L]()
{
luaL_checkudata(L, 1, w_render_table);
// assuming GLWindow udata is self and onrender_cb is your lua closure above
// access GLWindow.onrender_cb through GLWindows's metatable
lua_getfield(L, 1, "onrender_cb");
qDebug() << "onrender";
luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure
lua_call(L, 0, 0);
});
return 0;
}
Edit: After thinking about this some more, it probably makes more sense to create a lua reference using luaL_ref. This way you don't have to care what happens to be on the stack when self->onrender actually runs, which I'm assuming is async:
int w_render_onrender(lua_State *L)
{
luaL_checkudata(L, 1, w_render_table);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
auto lua_cb = luaL_ref(L, LUA_REGISTRYINDEX);
// just to check that what's on the stack shouldn't matter
lua_settop(L, 0);
self->onrender([L, lua_cb]()
{
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_cb);
luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure
qDebug() << "onrender";
lua_call(L, 0, 0);
luaL_unref(L, LUA_REGISTRYINDEX, lua_cb); // assuming you're done with it
});
return 0;
}
I am trying to get values from a Lua table. This is what I have written in Program.cpp:
lua_State* lua = luaL_newstate();
luaL_openlibs(lua);
luaL_dofile(program->getLuaState(), "Script.lua");
lua_getglobal(lua, "table");
lua_pushstring(lua, "x");
lua_gettable(lua, -2);
printf("%i", lua_tonumber(lua, -1));
And I wrote this in Script.lua:
table = {x = 12, y = 32}
The problem is that this only writes 0 in the console. I have checked that the lua file is loading correctly. What am I doing wrong?
Change %i to %g. lua_tonumber returns a float or double, not an int.