[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Lua Wrapping
 
- From: David Morris-Oliveros <david@...>
 
- Date: Thu, 04 Aug 2005 12:40:43 +1000
 
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.
------------------------------------------------------------------------