如何在userdata中存储值类型?

时间:2015-04-30 01:53:04

标签: c++ c lua lua-5.2

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

在我的代码中,我都不知道如何正确附加值类型yz

1 个答案:

答案 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),然后在那里查找字段。