[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Creating userdata interfaces
- From: Jerome Vuarand <jerome.vuarand@...>
- Date: Fri, 23 Oct 2009 18:55:15 +0200
2009/10/23 Chris Gagnon <cgagnon@zindagigames.com>:
> I must be making this harder then it is but I'm not sure how to lay it out.
>
> I have this:
>
> luaL_newmetatable(L, "Vector");
>
> lua_pushliteral(L, "__index");
> lua_pushcfunction(L, LuaVectorGet);
> lua_settable(L, -3);
>
> lua_pushliteral(L, "__newindex");
> lua_pushcfunction(L, LuaVectorSet);
> lua_settable(L, -3);
>
> lua_newtable(L);
> int nFuncIndex = lua_gettop(L);
>
> lua_pushvalue(L, nFuncIndex);
> lua_setfield(L, LUA_GLOBALSINDEX, msc_pType);
>
> lua_newtable(L);
> int mt = lua_gettop(L);
> lua_pushliteral(L, "__call");
> lua_pushcfunction(L, LuaNew);
> lua_pushliteral(L, "new");
> lua_pushvalue(L, -2); // duplicate LuaNew
> lua_settable(L, nFuncIndex);
> lua_settable(L, mt);
> lua_setmetatable(L, nFuncIndex);
> lua_pop(L, 2);
>
> User data created like so:
> float * fUserData = (float *)lua_newuserdata(L, 4 * sizeof(float));
> luaL_getmetatable(L, "Vector");
> lua_setmetatable(L, -2);
> return fUserData;
>
> This gives me the interface:
> local v = Vector.new(1,1,1)
> local vec = Vector(1,1,2)
> local x = vec.x
>
> the problem is when i want a Length() function
>
> if i switch the __index block to:
>
> lua_pushliteral(L, "__index");
> lua_newtable(L);
> lua_pushliteral(L, "Length");
> lua_pushcfunction(L, LuaLength);
> lua_settable(L, -3);
>
> lua_pushliteral(L, "__index");
> lua_pushcfunction(L, LuaGet);
> lua_settable(L, -3);
>
> lua_pushvalue(L,-1);
> lua_setmetatable(L, -2);
> lua_settable(L, nMetatableIndex);
>
> i now get the interface:
> vec:Length()
>
> however the vec.x interface is broken since the __index gets the table on
> the stack not my userdata.
>
> Hopefully that was clear, i appreciate any comments/ help on a fix for this
> or on better ways to build what I'm trying to do.
When I need to define both accessors (e.g. your LuaVectorGet) and
static fields (a classic __index table), I usually use a generic index
function that accesses other fields of the metatable :
int lua__generic___index(lua_State* L)
{
lua_getmetatable(L, 1);
lua_getfield(L, -1, "getters");
if (!lua_isnil(L, -1))
{
lua_pushvalue(L, 2);
lua_gettable(L, -2);
if (!lua_isnil(L, -1))
{
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
return 1;
}
lua_pop(L, 1); // getter
}
lua_pop(L, 1); // getters
lua_getfield(L, -1, "methods");
lua_pushvalue(L, 2);
lua_gettable(L, -2);
return 1;
}
int lua__generic___newindex(lua_State* L)
{
lua_getmetatable(L, 1);
lua_getfield(L, -1, "setters");
if (!lua_isnil(L, -1))
{
lua_pushvalue(L, 2);
lua_gettable(L, -2);
if (!lua_isnil(L, -1))
{
lua_pushvalue(L, 1);
lua_pushvalue(L, 3);
lua_call(L, 2, 0);
return 0;
}
lua_pop(L, 1); // setter
}
lua_pop(L, 1); // setters
return luaL_error(L, "invalid key");
}
Note that the keys of the methods, setters and getters tables can be
any type, this can be useful if you want your v.x getter also work
with v[1].
Here I throw an error when trying to write to an unsupported key (one
without setter), but you could store that new pair in a table
associated to the userdata (for exemple its environment), and return
them from __index. This would allow adding custom attributes from Lua
to a userdata object. Just ask if you would like to see such an
enhanced version.