lua-users home
lua-l archive

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


First of all, sorry for the very very long post. If things like these are considered bad, please let me know. Thanks

I'm trying to wrap some C++ objects in my app, and I want to, in Lua code, be able to do this:

v = Vec2:new();
len = v.length();
v.x = 10.1;
xpos = v.x;

To this effect, i have come up with a scheme, obviously wrong. It works under pure lua, but when i try to implement it in C++, it falls flat.

My idea is this:
I create a table (Vec2), that has a series of methods that the class needs (length, angle, etc). Then, i create another table (annonymous) that contains 2 metamethods: __index & __newindex. These access the raw C++ land variables and act upon it.

My idea is thus:

When I call:
v = Vec2:new()
The table 'Vec2' contains the function 'new' and calls it. This returns a table (in lua) or userdata (in C++) whose metatable is annonymous and has 2 metamethods: __index & __newindex that both point to Vec2.

So, when i call this:
v.x = 1;
I expect this to happen: 'v' doesn't have something called 'x', so it goes to its metatable and calls __index, which is Vec2, but it doesn't have it either, so it goes to its (Vec2's) metatable, which is the annonymous one. Since this last one has __index, it calls it. And all is fine.

With the exception that in C++ land, i get a table instead of the expected 'v' userdata.

This is what the pure lua code looks like:
-- lua space -----------------------------------------------------------

Vec2 =
{
   length = function (self)
       return math.sqrt(self.x*self.x + self.y*self.y)
   end;

   new = function (self, x,y)
       return setmetatable({}, { __index = Vec2, __newindex = Vec2 });
   end;
__tostring = function (self)
       return "[Vec2]";
   end;
}

setmetatable(Vec2, {
   __index = function (table, key)
       print ("Trying to index ", table, "with key", key);
       if key == "x" then return 21; end;
       if key == "y" then return 12; end;
       return 0;
   end;

   __newindex = function (table, key, value)
       print ("Trying to newindex ", table, "with key", key, " = ", value);
   end;
   }
);

When I try to implement this in C++ land, i do the same thing:
-- C++ space -----------------------------------------------------------

class Vec4
{
public:
   Vec4() {}
Vec4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) {}
   Vec4(const Vec4& o) : x(o.x), y(o.y), z(o.z), w(o.w) {}
   float length() { return (float)sqrtf(x*x + y*y + z*z + w*w); }
   float x,y,z,w;
};

typedef struct { Vec4 *pT; } userdata_Vec4;

//----------------------------------------------------------------
int Vec4_new(lua_State* L)
{
   lua_remove(L, 1);   // use classname:new(), instead of classname.new()

userdata_Vec4 *ud = static_cast< userdata_Vec4* > (lua_newuserdata(L, sizeof(userdata_Vec4))); // push 1, pop 0

   int udidx = lua_gettop(L);

   ud->pT = new Vec4();

   luaL_getmetatable(L, "Vec4");

   // sets the metatable of the new userdata_Vec4 to the
   // metatable stored in the registry
   lua_setmetatable (L, udidx);        // push 0, pop 1

   // we got still one object on the stack, the userdata_Vec4,
   // which is what we return, so tell lua this.
   return 1;
}

//----------------------------------------------------------------
int Vec4_gc(lua_State* L)
{
   if (lua_gettop(L) != 1)
   {
       lua_pushstring(L, "incorrect argument to method 'Vec4_gc'");
       lua_error(L);
   }

   userdata_Vec4 *udv4t =
       static_cast<userdata_Vec4*>(luaL_checkudata(L, 1, "Vec4"));

   delete udv4t->pT;
   return 0;
}


//----------------------------------------------------------------
int Vec4_index(lua_State* L)
{
   if (lua_gettop(L) != 2)
   {
       lua_pushstring(L, "incorrect argument to method 'Vec4_index'");
       lua_error(L);
   }

   userdata_Vec4* ud =
       static_cast<userdata_Vec4*>(luaL_checkudata(L, 1, "Vec4"));
   const char * indexing = lua_tostring(L,2);

   if (indexing[1] != 0)
   {
       lua_pushstring(L, "incorrect argument to method 'Vec4_index'");
       lua_error(L);
   }

   switch(indexing[0])
   {
   case 'x': lua_pushnumber(L, (LUA_NUMBER) ud->pT->x); break;
   case 'y': lua_pushnumber(L, (LUA_NUMBER) ud->pT->y); break;
   default: {
       lua_pushfstring(L, "Don't know about '%s'", indexing);
       lua_error(L);
       return 0;
            }
   }

   return 1;
}

//----------------------------------------------------------------
int Vec4_newindex(lua_State* L)
{
   dumpStack(L);

   if (lua_gettop(L) != 3)
   {
       lua_pushstring(L, "incorrect argument to method 'Vec4_index'");
       lua_error(L);
   }

   userdata_Vec4* ud =
       static_cast<userdata_Vec4*>(luaL_checkudata(L, 1, "Vec4"));
   const char * indexing = lua_tostring(L,2);
   double value = luaL_checknumber(L,3);

   if (indexing[1] != 0) // only a one letter thing
   {
lua_pushfstring(L, "incorrect argument to method 'Vec4_index' [%s]", indexing);
       lua_error(L);
   }

   switch(indexing[0])
   {
   case 'x': ud->pT->x = (float) value; break;
   case 'y': ud->pT->y = (float) value; break;
   default: {
       lua_pushfstring(L, "Don't know about '%s'", indexing);
       lua_error(L);
       return 0;
            }
   }

   return 0;
}

//----------------------------------------------------------------
int Vec4_tostring(lua_State* L)
{
   if (lua_gettop(L) != 1)
   {
       lua_pushstring(L, "incorrect argument to method 'Vec4_tostring'");
       lua_error(L);
   }

   userdata_Vec4* ud =
       static_cast<userdata_Vec4*>(luaL_checkudata(L, 1, "Vec4"));
   if (ud)
   {
lua_pushfstring(L, "<%f, %f, %f, %f>", ud->pT->x, ud->pT->y, ud->pT->z, ud->pT->w);
       return 1;
   }

   lua_pushstring(L, "Vec4 descriptor");
   return 1;
}

//----------------------------------------------------------------
int Vec4_length(lua_State* L)
{
   if (lua_gettop(L) != 1)
   {
       lua_pushstring(L, "incorrect argument to method 'Vec4_length'");
       lua_error(L);
   }

   userdata_Vec4* ud =
       static_cast<userdata_Vec4*>(luaL_checkudata(L, 1, "Vec4"));
   lua_pushnumber(L, (LUA_NUMBER)ud->pT->length());

   return 1;
}

//----------------------------------------------------------------
int bindLua(lua_State* L)
{
   // create table
   lua_newtable (L);
   int Vec2_ct = lua_gettop (L);
   // [anonTable] = {}
   lua_newtable (L);
   int Vec2_mt = lua_gettop (L);
   // create a metatable
   luaL_newmetatable(L, "Vec2");
   int Vec2_inst_mt = lua_gettop (L);

   // give the table a name
   lua_pushstring (L, "Vec2");
   lua_pushvalue (L, Vec2_ct);
   lua_settable (L, LUA_GLOBALSINDEX);

   // [anonTable].__index = Vec2_index
   lua_pushstring (L, "__index");
   lua_pushcfunction (L, Vec2_index);
   lua_settable (L, Vec2_mt);

   // [anonTable].__newindex = Vec2_newindex
   lua_pushstring (L, "__newindex");
   lua_pushcfunction (L, Vec2_newindex);
   lua_settable (L, Vec2_mt);

   // [anonTable].__gc = Vec2_gc
   lua_pushliteral(L, "__gc");
   lua_pushcfunction(L, Vec2_gc);
   lua_settable(L, Vec2_mt);

   // [anonTable].__tostring = Vec2_tostring
   lua_pushliteral(L, "__tostring");
   lua_pushcfunction(L, Vec2_tostring);
   lua_settable(L, Vec2_mt);

   lua_pushliteral (L, "new");
   lua_pushcfunction (L, Vec2_new);
   lua_settable (L, Vec2_ct);

   // methods

   lua_pushliteral (L, "length");
   lua_pushcfunction (L, Vec2_length);
   lua_settable (L, Vec2_ct);

   // Vec2.__metatable = [anonTable]
   lua_pushvalue (L, Vec2_mt);
   lua_setmetatable (L, Vec2_ct);


   lua_pushstring(L, "__index");
   lua_pushvalue(L, Vec2_ct);
   lua_settable(L, Vec2_inst_mt);

   lua_pushstring(L, "__newindex");
   lua_pushvalue(L, Vec2_ct);
   lua_settable(L, Vec2_inst_mt);

   lua_pop (L, 3);
}


And when i try to run this Lua code:

-- lua space -----------------------------------------------------------

> v = Vec2:new()
> print(v)
userdata: 0093FC48
> v.x = 1;


CRASH!
Upon inspecting the stack, i see this:
Dumping stack [3]
      Arg [1][table][-]
      Arg [2][string][x]
      Arg [3][string][1]

So my question is:
Why is the 1st thing on the stack a table and not the userdata?


--
// David Morris-Oliveros


------------------------------------------------------------------------
Contact:
Team Bondi Pty Ltd
Level 2, 608 Harris Street
Ultimo, NSW 2007
Australia
Tel: +61 (0)2 8218 1500
Fax: +61 (0)2 8218 1507
Web: http://www.teambondi.com
------------------------------------------------------------------------
This email may contain confidential information.  If you are not
the intended recipient, you may not copy or deliver this message to
anyone. In such case, you should destroy this message and kindly
notify the sender by reply email. Opinions, conclusions and other
information in this message that do not relate to the official business
of our firm shall be understood as neither given nor endorsed by it.
------------------------------------------------------------------------