2015-04-30 34 views
1

This SO article is the same thing,但答案是無益的,因爲答案是在Lua中,問題是關於C-API。所以我再問一次。希望其他人能從這個問題中受益。如何在用戶數據中存儲值類型?

實際上,我有2個問題(我不能得到Y如果Z到工作,我不能得到的HelloWorld()工作)

我試圖去這樣的:

local x = MyCBoundLib.GetSomething() 
print(x.y) 
print(x.z) 

其中x是用戶數據。我不斷收到attempt to index a userdata value

我知道「userdata isn't indexable without a metatable because it's C/C++ data

在我的C代碼,我做這樣的事情,試圖包裝的對象。

int push_Something(lua_State *L, void *object) 
{ 
    struct SomethingWrapper *w = (struct SomethingWrapper *)lua_newuserdata(L, sizeof(struct SomethingWrapper)); 
    w->object = object; 

    luaL_setmetatable(L, "Something"); 
    return 1; 
} 

早些時候,我試着註冊了一個名爲Something元表,就像這樣:

luaL_newmetatable(L, "Something"); 
lua_pushvalue(L, -1); 
lua_setfield(L, -2, "__index"); 
luaL_setfuncs(L, some_funcs, 0); 
lua_pop(L, 1); 

其中some_funcs有:

static luaL_Reg const some_funcs [] = 
{ 
    { "helloworld",  l_helloworld }, 
    { NULL, NULL } 
}; 

如果我嘗試print(x.helloworld()),我得到了同樣的錯誤: attempt to index a userdata value

在我的任何代碼中,我都不知道如何正確附加值類型yz

+0

您在使用'push_Something'之前註冊/創建了metatable? –

+0

正確,首先創建metatable – 010110110101

回答

4

首先,helloworld您的代碼工作:

/* file: hw.c 
* on Debian/Ubuntu compile with: 
* `gcc -I/usr/include/lua5.2 -fpic -shared -o hw.so hw.c` 
*/ 
#include <lua.h> 
#include <lauxlib.h> 

struct SomethingWrapper { 
    void *object; 
}; 

static int l_helloworld(lua_State *L) { 
    lua_pushliteral(L, "Hello World!"); 
    return 1; 
} 

static luaL_Reg const some_funcs[] = { 
    { "helloworld", l_helloworld }, 
    { NULL, NULL } 
}; 

int push_Something(lua_State *L, void *object) { 
    struct SomethingWrapper *w = lua_newuserdata(L, sizeof(*w)); 
    w->object = object; 
    luaL_setmetatable(L, "Something"); 
    return 1; 
} 

int luaopen_hw(lua_State *L) { 
    luaL_newmetatable(L, "Something"); 
    lua_pushvalue(L, -1); 
    lua_setfield(L, -2, "__index"); 
    luaL_setfuncs(L, some_funcs, 0); 
    lua_pop(L, 1); 

    push_Something(L, NULL); 
    return 1; 
} 

和測試腳本:

-- file: hwtest.lua 
local x = require("hw") 
print(x.helloworld()) 

輸出是:

Hello World!

有關您需要將用戶數據訪問性能將__index設置爲函數而不是表格。每當您嘗試訪問userdata上的字段時,都會使用兩個參數(userdata和key)調用該函數,並且您可以查詢您的C對象並推送所需的結果。

如果您打算同時支持方法和屬性,它會變得稍微複雜一些,但基本方法如下:使用函數__index metamethod。這個函數可以訪問一個方法表(例如通過一個upvalue或者註冊表等),並且試圖查找該表中給定的鍵。如果你成功了,你會返回這個價值。如果沒有提供任何內容,則將給定鍵與C對象的有效屬性名稱進行比較,如果得到匹配則返回相應的值。 (如果你沒有得到一場比賽,你可以返回nil或提出錯誤 - 這取決於你。)

這是一個可重用的實現,方法(從moon toolkit提取):

static int moon_dispatch(lua_State* L) { 
    lua_CFunction pindex; 
    /* try method table first */ 
    lua_pushvalue(L, 2); /* duplicate key */ 
    lua_rawget(L, lua_upvalueindex(1)); 
    if(!lua_isnil(L, -1)) 
    return 1; 
    lua_pop(L, 1); 
    pindex = lua_tocfunction(L, lua_upvalueindex(2)); 
    return pindex(L); 
} 

MOON_API void moon_propindex(lua_State* L, luaL_Reg const methods[], 
           lua_CFunction pindex, int nups) { 
    if(methods != NULL) { 
    luaL_checkstack(L, nups+2, "not enough stack space available"); 
    lua_newtable(L); 
    for(; methods->func; ++methods) { 
     int i = 0; 
     for(i = 0; i < nups; ++i) 
     lua_pushvalue(L, -nups-1); 
     lua_pushcclosure(L, methods->func, nups); 
     lua_setfield(L, -2, methods->name); 
    } 
    if(pindex) { 
     lua_pushcfunction(L, pindex); 
     if(nups > 0) { 
     lua_insert(L, -nups-2); 
     lua_insert(L, -nups-2); 
     } 
     lua_pushcclosure(L, moon_dispatch, 2+nups); 
    } else if(nups > 0) { 
     lua_replace(L, -nups-1); 
     lua_pop(L, nups-1); 
    } 
    } else if(pindex) { 
    lua_pushcclosure(L, pindex, nups); 
    } else { 
    lua_pop(L, nups); 
    lua_pushnil(L); 
    } 
} 

用法:

/* [ -nup, +1, e ] */ 
void moon_propindex(lua_State* L, 
        luaL_Reg const* methods, 
        lua_CFunction index, 
        int nup); 

This function is used for creating an __index metafield. If index is NULL but methods is not, a table containing all the functions in methods is created and pushed to the top of the stack. If index is not NULL, but methods is, the index function pointer is simply pushed to the top of the stack. In case both are non NULL, a new C closure is created and pushed to the stack, which first tries to lookup a key in the methods table, and if unsuccessful then calls the original index function. If both are NULL, nil is pushed to the stack. If nup is non-zero, the given number of upvalues is popped from the top of the stack and made available to all registered functions. (In case index and methods are not NULL, the index function receives two additional upvalues at indices 1 and 2.) This function is used in the implementation of moon_defobject, but maybe it is useful to you independently.

如果試圖存儲在用戶數據任意的Lua值(如標題所示) - 你不能。但是您可以將一個額外的表(稱爲「用戶值」)與每個用戶數據關聯並在其中存儲任意值。該方法類似於上面的方法,但不是與預定義的屬性名稱匹配並直接訪問C對象,而是首先推送用戶值表(使用lua_getuservalue),然後查找您的字段。

+0

用戶數據上的'__index'表應該可以正常工作,但它本身不允許訪問任何C端屬性。我很高興你說原始代碼的工作看起來很好,所以我很困惑。 –

+0

是的,但由於OP使用'luaL_newmetatable' /'luaL_setmetatable',metatable和'__index'元方法(函數或表)由此特定用戶數據類型的所有對象共享。如果你想使用'__index'表來實現屬性,你需要爲每個用戶數據對象分別使用metatables。 – siffiejoe

+0

同意。我只是迴應「爲了訪問用戶數據上的屬性,您需要將__index設置爲函數而不是表格。」這使得它聽起來(至少對我來說)桌子是行不通的。 –

相關問題