I am using [actually learning to use] the Lua C api. I’m new to Lua so apologies if I have some terminology incorrect, and would appreciate any corrections to it.
I have an empty Global table G which I create using lua_setglobal at some point on init. G’s __index points to a C function, which I believe is called a metamethod. When invoked, this function creates a new lightuserdata item that it inserts into the G global table.
So, if my understanding is correct, G.foo will result in an invocation of G’s __index metamethod and foo will be created by it and added to G. Future invocations to G.foo will no longer need to call the metamethod since it would find foo to exist in G.
Now, when creating foo, I associate a metatable to the newly created lightuserdata (foo) by setting its __index to an array of C functions which contains, most notably, ‘set’ and ‘get’. The idea is, whenever foo:get() is invoked, foo’s metatable should be looked up to call the C function to get its value, etc.
Here’s the (normal) behaviour I see:
-
Invoke G.foo from a lua file.
This creates foo using G’s metamethod as expected.
-
Then, invoke G.foo:get()
Since foo is already a part of G (previous step), G’s metamethod is not invoked, as expected. Instead, foo’s metatable is checked and the C function corresponding to ‘get’ is invoked. This is also as expected, and is exactly how I want it to work.
However, if I do this:
-
Invoke G.foo:get() directly without first invoking G.foo
Then, it invokes G’s metamethod twice, once for foo (expected) and once for ‘get’ (not expected). I don’t want ‘get’ to be processed by G’s __index metamethod. It basically tries to create a new lightuserdata called ‘get’ (just like it did for ‘foo’) and so on, which is not what I want to do. I’d like the newly created foo’s metatable to be looked up so that the proper ‘get’ C function is invoked for foo.
I’ve simplified my use case in order to make the issue most obvious, and so I hope its understandable enough. Also, would appreciate if you can point me to any lua documentation or feature reference that would help me understand why this happens.
Edit:
Adding some code with relevant parts to demonstrate what I am doing:
static void init()
{
lua_newtable( luaVM );
lua_createtable( luaVM, 0, 0 );
lua_pushcfunction( luaVM, lua_metaMethod );
lua_setfield( luaVM, -2, "__index" );
lua_setmetatable( luaVM, -2 );
lua_setglobal( luaVM, "G" );
}
static const luaL_reg lua_methods[] =
{
{ "set", lua_set },
{ "get", lua_get },
{0, 0}
};
static int lua_metaMethod( lua_State *luaVM )
{
// I get "foo" by using lua_tostring( luaVM, 2 ), and store that in 'name'.
// I then lookup 'fooData', which is a pointer to data associated with foo that I want to add to G.
lua_getglobal( "G" );
lua_pushlightuserdata( luaVM, ( void* ) fooData );
lua_createtable( luaVM, 0, 0 );
lua_createtable( luaVM, 0, 0 );
luaL_register( luaVM, NULL, lua_methods ); // Trying to make sure foo:get() calls one of these, etc.
lua_setfield( luaVM, -2, "__index" );
lua_setmetatable( luaVM, -2 );
lua_setfield( luaVM, -2, name );
return 1;
}
Given the code as you have described it, the problem is with
lua_metaMethod(BTW: it is a bad idea to name your functionslua_. That’s a prefix reserved for Lua API functions).The return value from the
__indexmetamethod will be returned to the user. So if the user saysG.foo, then the return value fromG‘s__indexmetamethod will be returned.What does
lua_metaMethodreturn? It returns exactly 1 return value. And since parameters passed to the function are the first on the stack, it will return the first parameter. The first parameter to the__indexmetamethod is always the table the metamethod is invoked on. In your case, this table is the table stored inG.Therefore,
G.foowill returnG. Therefore,G.foo:get()is equivalent toG:get()(though the first version does have an extra metamethod call). I’m guessing that’s not what you want 😉What it should return is the light userdata that was just stored in
G["foo"].Consider this code instead.