This SO article is the same thing,但答案是无益的,因为答案是在Lua中,问题是关于C-API。所以我再问一遍。希望其他人能从这个问题中受益。
我实际上遇到了2个问题(我无法让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
在我的代码中,我都不知道如何正确附加值类型y
或z
。
答案 0 :(得分: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
元方法。此函数可以访问方法表(例如,通过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 );
此函数用于创建__index元字段。如果index为NULL但方法不是,则创建包含方法中所有函数的表并将其推送到堆栈顶部。如果index不是NULL,但方法是,则只需将索引函数指针推送到堆栈顶部。如果两者都是非NULL,则创建一个新的C闭包并将其推送到堆栈,该堆栈首先尝试查找方法表中的键,如果不成功则调用原始索引函数。如果两者都为NULL,则将nil推送到堆栈。如果nup非零,则从堆栈顶部弹出给定数量的upvalues,并使其可用于所有已注册的函数。 (如果索引和方法不是NULL,索引函数会在索引1和2处接收两个额外的upvalues。)此函数用于moon_defobject的实现,但它可能对您有用。
如果您尝试在用户数据中存储任意Lua值(如标题所示) - 您无法做到。但是您可以将一个额外的表(称为&#34; uservalue&#34;)与每个用户数据相关联,并在那里存储任意值。该方法与上面的方法类似,但不是直接匹配预定义的属性名称并直接访问C对象,而是首先推送用户值表(使用lua_getuservalue
),然后在那里查找字段。