2012-03-22 40 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