2012-03-22 41 views
2

我正在使用[实际学习使用] Lua C api。我是Lua的新手,所以如果我有一些术语不正确,我会很感激,并希望对它有所更正。获取正确的Lua元方法来调用(C-api)

我有一个空的全局表G,我在init的某个时候使用lua_setglobal创建了这个表。 G的__index指向一个C函数,我相信它被称为metamethod。当被调用时,该函数会创建一个新的lightuserdata项目,并将其插入到G全局表格中。

因此,如果我的理解是正确的,G.foo将导致调用G的__index元方法,foo将由它创建并添加到G.未来对G.foo的调用将不再需要调用元方法因为它会发现foo存在于G.

现在,当创建foo时,我通过将其__index设置为一个C函数数组,将一个metatable与新创建的lightuserdata(foo)相关联,其中最着名的是'set '和'get'。的想法是,只要富:get()方法被调用时,Foo的元表,应抬头向调用C函数来获得它的值等

这里的(正常)的行为我看到:

  • 从lua文件中调用G.foo。

    这会按预期使用G的metamethod创建foo。

  • 然后,调用G.foo:get()

    由于foo是已经G(之前的步骤)的一部分,G的元方法不被调用,如所预期。相反,将检查foo的metatable,并调用与'get'对应的C函数。这也如预期的那样,并且正是我希望它工作的方式。

但是,如果我这样做:

  • 调用G.foo:get()的情况下直接调用第一G.foo

    然后,它调用G公司的元方法两次,一次为foo(预计)和一次'get'(不是预期的)。我不希望被'G'__index metamethod处理。它基本上试图创建一个名为'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

Lua是一个正确的名称。这意味着首字母大写。所以它是Lua,而不是Lua或LUA。 – 2012-03-22 02:44:47

+0

添加了一些代码到我原来的帖子来演示。看起来我的确有术语错误,对不起! – Varun 2012-03-22 03:09:24

回答

2

给予代码为你描述它,问题是lua_metaMethod(BTW:这是一个想法来命名你的功能lua_。这是一个为Lua API函数保留的前缀)。

元法的返回值将为返回给用户的。因此,如果用户说G.foo,那么将返回G的元方法的返回值__index metamethod。

lua_metaMethod返回什么?它只返回1个返回值。由于传递给函数的参数是堆栈中的第一个参数,它将返回第一个参数。元方法的第一个参数始终是metamethod被调用的表。在你的情况下,这个表格是存储在G中的表格。因此,G.foo将返回G。因此,G.foo:get()相当于G:get()(虽然第一个版本确实有一个额外的metamethod调用)。我猜这不是你想要的;)

它应该返回的是刚刚存储在G["foo"]中的light userdata。

请考虑使用此代码。

static int lua_metaMethod(lua_State *luaVM) 
{ 
    //We won't use this, so only do this if you need the string. 
    const char *name = lua_tostring(luaVM, 2); 

    // I then lookup 'fooData', which is a pointer to data associated with foo that I want to add to G. 

    //No need to get G. We already have it: the first parameter. 
    lua_pushlightuserdata(luaVM, (void*) fooData); //Stack: table, key, userdata 
    lua_createtable(luaVM, 0, 0);      //Stack: table, key, userdata, {} 
    lua_createtable(luaVM, 0, 0);      //Stack: table, key, userdata, {}, {} 
    luaL_register(luaVM, NULL, lua_methods);    // Trying to make sure foo:get() calls one of these, etc. 
    lua_setfield(luaVM, -2, "__index");     //Stack: table, key, userdata, {} 
    lua_setmetatable(luaVM, -2);      //Stack: table, key, userdata 

     //Stack now contains: table, key, userdata. 
    lua_insert(luaVM, 2);    //Stack: table, userdata, key 
    lua_pushvalue(luaVM, -2);   //Stack: table, userdata, key, userdata 
    lua_rawset(luaVM, 1);   //Use rawset because table has a metatable. It's probably best not to invoke it. 

     //Stack now contains: table, userdata. 
    lua_insert(luaVM, 1);     //Stack: userdata, table. 

    return 1; //Return just the userdata. `table` will be discarded. 
} 
+0

Nicol,感谢将Lua堆栈放入单词中,以帮助我更好地将其视觉化。我将养成这样做的习惯来调试。我现在明白我正在返回G,因此:get()会再次调用G的metamethod。 – Varun 2012-03-22 04:21:03

+0

我已经删除了lua_metaMethod,并在我的代码中使用了不含lua_前缀的名称,谢谢。此外,我现在已经完全按照预期工作了! – Varun 2012-03-22 04:24:18