lua-users home
lua-l archive

[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+%.)","")))'