How to store a value type in a userdata?

我们两清 提交于 2019-12-09 23:24:00

问题


This SO article is the same thing, but the answer is unhelpful because the answer was in Lua and the question was about the C-API. So I'm asking again. Hopefully, others will benefit from this question.

I'm actually having 2 problems (I can't get y an z to work, and I can't get helloworld() to work)

I'm trying to get to this:

local x = MyCBoundLib.GetSomething()
print(x.y)
print(x.z)

Where x is a userdata. I keep getting attempt to index a userdata value

I know that "userdata isn't indexable without a metatable because it's C/C++ data"

In my C-code, I do something like this to try and wrap the object.

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

Earlier, I tried to register a metatable called Something, like so:

luaL_newmetatable(L, "Something");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, some_funcs, 0);
lua_pop(L, 1);

Where some_funcs has:

static luaL_Reg const some_funcs [] =
{
    { "helloworld",     l_helloworld },
    { NULL, NULL }
};

If I try print(x.helloworld()), I get the same error: attempt to index a userdata value

In none of my code do I know how to properly attach the value type y or z.


回答1:


First, for helloworld your code works:

/* 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;
}

and the test script:

-- file: hwtest.lua
local x = require( "hw" )
print( x.helloworld() )

Output is:

Hello World!

For accessing properties on a userdata you need to set __index to a function instead of a table. The function is called with two arguments (the userdata, and the key) whenever you try to access a field on the userdata, and you can query your C object and push the desired result.

It gets a little more complicated if you intend to support methods and properties at the same time, but the basic approach is the following: You use a function as __index metamethod. This function has access to a table of methods (e.g. via an upvalue or the registry, etc.) and tries to lookup the given key in that table. If you succeed you return that value. If you come up with nothing, you compare the given key to the valid property names of your C object and return the respective values if you get a match. (If you don't get a match you can return nil or raise an error -- that's up to you.)

Here is a reusable implementation of that approach (extracted from the 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 );
  }
}

Usage:

/*  [ -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.

If you try to store arbitrary Lua values in a userdata (like the title suggests) -- you can't. But you can associate an extra table (called "uservalue") with each userdata and store arbitrary values there. The approach is similar to the one above, but instead of matching with predefined property names and accessing the C object directly you first push the uservalue table (using lua_getuservalue) and then lookup your field there.



来源:https://stackoverflow.com/questions/29957701/how-to-store-a-value-type-in-a-userdata

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!