lua-users home
lua-l archive

Re: Nested userdata

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi Jonathan,
On Mon, Feb 16, 2009 at 10:10:06AM -0800, Jonathan wrote:
> I would like to access the following C++ types from lua:
> 
> struct A { int x, y; };
> struct B { A a; }
> 
> and then in lua do:
> 
> b = B:new()
> var = b.a.x
> 
> So the best way I can explain this is that I want to have "nested" userdata. Here's what I have so far:
> 
> static int B_new (lua_State *L)
> {
> B *b = (B*)lua_newuserdata(L, sizeof(B));
> luaL_getmetatable(L, "B");
> lua_setmetatable(L, -2);
> return 1;
> }
> 
> static int A_index (lua_State *L)
> {
> // push 'x' or 'y' depending on key
> }
> 
> static int B_index (lua_State *L)
> {
> // what do I push if the key is 'a'? I don't want to allocate new userdata, but I also don't want light user data because I need the metatable.
> 
> if (lua_tostring(L, 2) == "a")
> // .. ? ..
> }
Try something like this:
#include <lua.h>
#include <lauxlib.h>
typedef struct { int x, y; } A;
typedef struct { A a; } B;
static A** A_push (lua_State *L, A* a) {
 A **p = (A**) lua_newuserdata(L, sizeof(A*));
 *p = a;
 luaL_getmetatable(L, "A");
 lua_setmetatable(L, -2);
 return p;
}
static int B_new (lua_State *L) {
 B *b = (B*) lua_newuserdata(L, sizeof(B));
 luaL_getmetatable(L, "B");
 lua_pushvalue(L, -2); /* b */
 A_push(L, &(b->a)); /* a */
 lua_rawset(L, -3); /* mt[b] = a */
 lua_setmetatable(L, -2);
 return 1;
}
static int A_index (lua_State *L) {
 A **a = (A**) lua_touserdata(L, 1);
 const char *k = luaL_checkstring(L, 2);
 if (*k == 'x') lua_pushinteger(L, (*a)->x);
 else if (*k == 'y') lua_pushinteger(L, (*a)->y);
 else luaL_error(L, "unknown key: %s", k);
 return 1;
}
static int A_newindex (lua_State *L) {
 A **a = (A**) lua_touserdata(L, 1);
 const char *k = luaL_checkstring(L, 2);
 int v = luaL_checkinteger(L, 3);
 if (*k == 'x') (*a)->x = v;
 else if (*k == 'y') (*a)->y = v;
 else luaL_error(L, "unknown key: %s", k);
 return 0;
}
static int B_index (lua_State *L) {
 const char *k = luaL_checkstring(L, 2);
 if (*k == 'a') {
 luaL_getmetatable(L, "B");
 lua_pushvalue(L, 1); /* b */
 lua_rawget(L, -2);
 }
 else luaL_error(L, "unknown key: %s", k);
 return 1;
}
int luaopen_nestedudata (lua_State *L) {
 /* A */
 luaL_newmetatable(L, "A");
 lua_pushcfunction(L, A_index);
 lua_setfield(L, -2, "__index");
 lua_pushcfunction(L, A_newindex);
 lua_setfield(L, -2, "__newindex");
 /* B */
 luaL_newmetatable(L, "B");
 lua_newtable(L);
 lua_pushliteral(L, "k");
 lua_setfield(L, -2, "__mode");
 lua_setmetatable(L, -2); /* B's MT is now weak-keyed */
 lua_pushcfunction(L, B_index);
 lua_setfield(L, -2, "__index");
 lua_pushcfunction(L, B_new);
 return 1;
}
Test:
$ lua
Lua 5.1.3 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> new = require"nestedudata"
> b = new()
> b.a.x, b.a.y = 1, -1
> print(b, b.a, b.a.x, b.a.y)
userdata: 0x9a4da74 userdata: 0x9a4db54 1 -1
Cheers,
Luis
-- 
Computers are useless. They can only give you answers.
 -- Pablo Picasso
-- 
Luis Carvalho (Kozure)
lua -e 'print((("lexcarvalho@NO.gmail.SPAM.com"):gsub("(%u+%.)","")))'

AltStyle によって変換されたページ (->オリジナル) /