如何在userdata中存储值类型?

这篇SO文章是一回事,但答案是无益的,因为答案是在Lua中,问题是关于C-API。 所以我再问一遍。 希望其他人能从这个问题中受益。

我实际上有两个问题(我不能让你工作,我不能让helloworld()工作)

我想要达到这个目的:

local x = MyCBoundLib.GetSomething() print(xy) print(xz) 

其中x是用户数据。 我一直attempt to index a userdata value

我知道“ 没有metatable , userdata是不可索引的,因为它是C / C ++数据 ”

在我的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的metatable,如下所示:

 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

首先,对于helloworld您的代码可以工作:

 /* file: hw.c * on Debian/Ubuntu compile with: * `gcc -I/usr/include/lua5.2 -fpic -shared -o hw.so hw.c` */ #include  #include  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() ) 

输出是:

你好,世界!

要访问用户数据的属性,您需要将__index设置为函数而不是表。 每当您尝试访问userdata上的字段时,都会使用两个参数(userdata和key)调用该函数,并且可以查询C对象并推送所需的结果。

如果您打算同时支持方法和属性,它会变得有点复杂,但基本方法如下:您使用函数作为__index元方法。 此函数可以访问方法表(例如,通过upvalue或注册表等),并尝试查找该表中的给定键。 如果成功,则返回该值。 如果您没有任何结果,则将给定的键与C对象的有效属性名称进行比较,如果得到匹配则返回相应的值。 (如果你没有得到匹配,你可以返回nil或引发错误 – 这取决于你。)

以下是该方法的可重用实现(从月球工具包中提取):

 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 ); 

此函数用于创建__index元字段。 如果index为NULL但方法不是,则创建包含方法中所有函数的表并将其推送到堆栈顶部。 如果index不是NULL,但方法是,则只需将索引函数指针推送到堆栈顶部。 如果两者都是非NULL,则创建一个新的C闭包并将其推送到堆栈,该堆栈首先尝试查找方法表中的键,如果不成功则调用原始索引函数。 如果两者都为NULL,则将nil推送到堆栈。 如果nup非零,则从堆栈顶部弹出给定数量的upvalues,并使其可用于所有已注册的函数。 (如果索引和方法不是NULL,索引函数会在索引1和2处接收两个额外的upvalues。)此函数用于moon_defobject的实现,但它可能对您有用。

如果您尝试在用户数据中存储任意Lua值(如标题所示) – 您不能。 但是您可以将一个额外的表(称为“uservalue”)与每个用户数据相关联,并在那里存储任意值。 该方法与上面的方法类似,但不是直接匹配预定义的属性名称并直接访问C对象,而是先推送uservalue表(使用lua_getuservalue ),然后在那里查找字段。