我正在使用[实际学习使用] Lua C api。我是Lua的新手,如果我的术语不正确,我会道歉,并希望对它进行任何更正。
我有一个空的全局表G,我在init上的某个时刻使用lua_setglobal创建。 G的__index指向一个C函数,我认为它被称为元方法。调用时,此函数会创建一个新的lightuserdata项,并将其插入G全局表中。
所以,如果我的理解是正确的,G.foo将导致G的__index元方法的调用,foo将由它创建并添加到G.对G.foo的未来调用将不再需要调用metamethod它会发现f存在于G.
现在,在创建foo时,我将metatable与新创建的lightuserdata(foo)相关联,方法是将其__index设置为C函数数组,其中包含最值得注意的是'set'和'get'。这个想法是,每当调用foo:get()时,应该查找foo的metatable来调用C函数来获取它的值等。
这是我看到的(正常)行为:
从lua文件中调用G.foo。
这会使用G的metamethod按预期创建foo。
然后,调用G.foo:get()
由于foo已经是G的一部分(上一步),因此不会按预期调用G的元方法。相反,检查foo的metatable并调用与'get'对应的C函数。这也是预期的,这正是我希望它发挥作用的方式。
但是,如果我这样做:
直接调用G.foo:get()而不先调用G.foo
然后,它调用G的metamethod两次,一次用于foo(预期),一次用于'get'(不是预期的)。我不希望G'的__index元方法处理'get'。它基本上试图创建一个名为“get”的新lightuserdata(就像它为'foo'做的那样)等等,这不是我想要做的。我想要查找新创建的foo的metatable,以便为foo调用正确的'get'C函数。
为了使问题最明显,我简化了我的用例,所以我希望它足够容易理解。此外,如果您能指出任何lua文档或功能参考,以帮助我理解为什么会发生这种情况,我将不胜感激。
编辑: 添加一些带有相关部分的代码来演示我在做什么:
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;
}
答案 0 :(得分:3)
考虑到你所描述的代码,问题在于lua_metaMethod
(顺便说一句:错误的想法命名你的函数lua_
。这是一个保留的前缀对于Lua API函数)。
__index
元方法的返回值将返回给用户。因此,如果用户说G.foo
,则会返回G
的{{1}}元方法的返回值。
__index
返回什么?它返回正好1个返回值。由于传递给函数的参数是堆栈中的第一个参数,因此它将返回第一个参数。 lua_metaMethod
元方法的第一个参数始终是调用元方法的表。在您的情况下,此表是存储在__index
。
因此,G
将返回 G.foo
。因此,G
相当于G.foo:get()
(尽管第一个版本确实有一个额外的元方法调用)。我猜这不是你想要的;)
它应该返回的是仅存储在G:get()
中的轻用户数据。
请考虑使用此代码。
G["foo"]