2015-04-30 35 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设置为函数而不是表格。”这使得它听起来(至少对我来说)桌子是行不通的。 –

相关问题