c++ - 如何在用户数据中存储值类型?

标签 c++ c lua lua-5.2

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

最佳答案

首先,对于 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 设置为函数而不是表。每当您尝试访问用户数据上的字段时,都会使用两个参数(用户数据和 key )调用该函数,您可以查询 C 对象并推送所需的结果。

如果您打算同时支持方法和属性,它会变得有点复杂,但基本方法如下:您使用一个函数作为 __index 元方法。此函数可以访问方法表(例如,通过上值或注册表等)并尝试在该表中查找给定的键。如果成功,则返回该值。如果您一无所获,则将给定键与 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),然后在那里查找您的字段。

关于c++ - 如何在用户数据中存储值类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29957701/

相关文章:

c++ - 我可以像使用 Lua 一样使用 Ruby 编写 C++ 应用程序脚本吗?

c++ - 编程语言究竟如何与脚本语言交互?

C++ std::function 参数 const 引用

c++ - 使用 IFileOperation 复制文件 (C++)

c++ - getline 与 int C++

c - 绘制球体时,无法使用 OpenGL 进行绘制

c - 读取C语言文本文件

c++ - xcode 从源代码中删除奇怪的控制字符

c - 如何找到具有特定值的行或列的总和?

Lua - 为什么允许函数调用后的字符串?