Lua 5 and tags

classic Classic list List threaded Threaded
29 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: Lua 5 and tags

Mark Hamburg-4
on 7/25/04 2:16 PM, Vincent Penne at [hidden email] wrote:

> Here is the code that do what I suggest :
> 
> void *luaL_checkudata_ref(lua_State *L, int ud, int ref, char *msg)
> {
> void *u;
> if (lua_getmetatable(L, ud)) && (lua_getref(L, ref), 1) &&
>     lua_rawequal(L, -1, -2) &&
>     (u = lua_touserdata(L, ud))) {
>   lua_pop(L, 2);
>   return u;
> }
> luaL_argerror(L, 0, msg); /* Never returns. */
> return NULL;
> }

The only problem with this code is that if you create multiple Lua universes
-- i.e., independent Lua states as opposed to multiple Lua threads -- then
you can't rely on sharing a C global for the ref to the metatable.

One other approach would be to assume that you could use location 1 in the
metatable and do a lua_rawgeti to fetch it and test that it held an
appropriate userdata value.

Untested...

void *luaL_checkudata_ud1 (lua_State *L, int ud, void *tident, char *msg)
{
    void *u = lua_touserdata(L, ud);
    if( u && lua_getmetatable(L, ud) ) {
        lua_rawgeti(L, -1, 1);
        if( lua_touserdata(L, -1) == tident ) {
            lua_pop(L, 2); /* Restore stack before fetching */
            return u;
        }
    }
    luaL_argerror(L, ud, msg);
    return NULL;
}

Mark


Reply | Threaded
Open this post in threaded view
|

Re: Lua 5 and tags

Vincent Penne
Mark Hamburg wrote:

on 7/25/04 2:16 PM, Vincent Penne at [hidden email] wrote:

Here is the code that do what I suggest :

void *luaL_checkudata_ref(lua_State *L, int ud, int ref, char *msg)
{
void *u;
if (lua_getmetatable(L, ud)) && (lua_getref(L, ref), 1) &&
   lua_rawequal(L, -1, -2) &&
   (u = lua_touserdata(L, ud))) {
 lua_pop(L, 2);
 return u;
}
luaL_argerror(L, 0, msg); /* Never returns. */
return NULL;
}

The only problem with this code is that if you create multiple Lua universes
-- i.e., independent Lua states as opposed to multiple Lua threads -- then
you can't rely on sharing a C global for the ref to the metatable.

Actually, what I do is put all the references not into global variables but into a C structure. And I instantiate one of these C binding structure for each lua state. Then to get that C binding structure, I can either store it as upvalue of my function, either have a function that map from a lua state adress to the binding structure. It is one of the case where light userdata are very handy actually ! When you use them as upvalue it's completely safe.

One other approach would be to assume that you could use location 1 in the
metatable and do a lua_rawgeti to fetch it and test that it held an
appropriate userdata value.

Untested...

void *luaL_checkudata_ud1 (lua_State *L, int ud, void *tident, char *msg)
{
   void *u = lua_touserdata(L, ud);
   if( u && lua_getmetatable(L, ud) ) {
       lua_rawgeti(L, -1, 1);
       if( lua_touserdata(L, -1) == tident ) {
           lua_pop(L, 2); /* Restore stack before fetching */
           return u;
       }
   }
   luaL_argerror(L, ud, msg);
   return NULL;
}


That's an idea ! I'm going to test that and see how it performs compared to the other method.


However, what I do in my binding (it is automatically generated) is a bit more complicated because it's a C++ binding, and I have to take care of type casting. My functions need to check the type in a more complex way because they can accept the base type or any of it's derivated type.

To do that, I construct a table of compatible type for each type. I store a reference on these compatible type table and try to index it with the metatable of the actual argument of the function, if the result is not nil then I know the type I received as input is compatible. Here is how my function looks like :

The lua_type_t structure comes from a superstructure (my binding structure) containing one of this lua_type_t object for each existing type, and the binding structure as been stored as upvalue for example.

static void * _lua_to_class(lua_State * __S__, int pos, lua_type_t * wanted_type)
{
 int iscompat;
#if 1 /* this part can be ifdefed out, but it's faster to check the special case where wanted type is exactly the given type */
 if (!lua_getmetatable(__S__, pos)) {
   return 0;
 }
 lua_getref(__S__, wanted_type->metatable);
 iscompat = lua_rawequal(__S__, -1, -2);
 lua_pop(__S__, 2);

 if (iscompat) {
   return *(void * *)lua_touserdata(__S__, pos);
 } else
#endif
 { /* from now on we check it is a compatible (derivated) type */
lua_getref(__S__, wanted_type->cttable); /* get the compatible type table */
   lua_getmetatable(__S__, pos);
lua_gettable(__S__, -2); /* try to get cttable[metatable] */
   iscompat = !lua_isnil(__S__, -1);
   lua_pop(__S__, 2);
   if (iscompat)
     return *(void * *)lua_touserdata(__S__, pos);
/*     printf("bind-lua : could not cast from %s to %s !\n",  */
/*        tn, wanted_type->name); */
   return 0;
 }
}




Reply | Threaded
Open this post in threaded view
|

Re: Lua 5 and tags

Mark Hamburg-4
If you can live with single-inheritance, my scheme using indices in the
metatable will work with the type-testing technique used in Oberon. Single
inheritance allows us to know that a class is at a particular depth in the
inheritance tree. The metatable for a class can then include the appropriate
light userdata for each class in its inheritance hierarchy. Testing for
class membership then becomes something like:

    luaObj.metatable[ classInfo->depth ] == classInfo

Mark


Reply | Threaded
Open this post in threaded view
|

Re: Lua 5 and tags

Jamie Webb-3
On Mon, Jul 26, 2004 at 08:17:20AM -0700, Mark Hamburg wrote:
> If you can live with single-inheritance, my scheme using indices in the
> metatable will work with the type-testing technique used in Oberon. Single
> inheritance allows us to know that a class is at a particular depth in the
> inheritance tree. The metatable for a class can then include the appropriate
> light userdata for each class in its inheritance hierarchy. Testing for
> class membership then becomes something like:
> 
>     luaObj.metatable[ classInfo->depth ] == classInfo

You can do something rather similar that works with multiple
inheritance by checking something like:

luaObj.metatable[ pushlightuserdata(classInfo) ] == true

-- Jamie Webb

Reply | Threaded
Open this post in threaded view
|

Re: Lua 5 and tags

Mark Hamburg-4
Good point. Slightly more expensive (a push and a hash lookup rather than an
indexing) but much more general.

Mark

on 7/26/04 9:13 AM, Jamie Webb at [hidden email] wrote:

> On Mon, Jul 26, 2004 at 08:17:20AM -0700, Mark Hamburg wrote:
>> If you can live with single-inheritance, my scheme using indices in the
>> metatable will work with the type-testing technique used in Oberon. Single
>> inheritance allows us to know that a class is at a particular depth in the
>> inheritance tree. The metatable for a class can then include the appropriate
>> light userdata for each class in its inheritance hierarchy. Testing for
>> class membership then becomes something like:
>> 
>>     luaObj.metatable[ classInfo->depth ] == classInfo
> 
> You can do something rather similar that works with multiple
> inheritance by checking something like:
> 
> luaObj.metatable[ pushlightuserdata(classInfo) ] == true
> 
> -- Jamie Webb


Reply | Threaded
Open this post in threaded view
|

Re: Lua 5 and tags

Vincent Penne
In reply to this post by Jamie Webb-3
Jamie Webb wrote:

On Mon, Jul 26, 2004 at 08:17:20AM -0700, Mark Hamburg wrote:
If you can live with single-inheritance, my scheme using indices in the
metatable will work with the type-testing technique used in Oberon. Single
inheritance allows us to know that a class is at a particular depth in the
inheritance tree. The metatable for a class can then include the appropriate
light userdata for each class in its inheritance hierarchy. Testing for
class membership then becomes something like:

   luaObj.metatable[ classInfo->depth ] == classInfo

You can do something rather similar that works with multiple
inheritance by checking something like:

luaObj.metatable[ pushlightuserdata(classInfo) ] == true

-- Jamie Webb


I see the idea. It looks good since it avoids the lua_getref in my solution. I'm going to try that ... And yes, I have multiple inheritance so I cannot use the first method. But it's interesting too.



Reply | Threaded
Open this post in threaded view
|

Re: Lua 5 and tags

Edgar Toernig
In reply to this post by Mark Hamburg-4
Mark Hamburg wrote:
>
> If you can live with single-inheritance, my scheme using indices in the
> metatable will work with the type-testing technique used in Oberon. Single
> inheritance allows us to know that a class is at a particular depth in the
> inheritance tree. The metatable for a class can then include the appropriate
> light userdata for each class in its inheritance hierarchy. Testing for
> class membership then becomes something like:
> 
>     luaObj.metatable[ classInfo->depth ] == classInfo

What I'm using (in Sol): every class table has an is_<ClassName> field
which is set to true.  Because of the inheritance, these fields are
inherited too.  Checks are simply:

   assert(obj:is_Foo)  -- implies obj:is_Table if Foo is derived from Table
or
   if obj:is_Table then ... end

It's a bit ugly because of the classname namespace and it may be slow
with deep hierarchies (unless the inheritance-resolver dynamically copies
fields to derived classes) but it was so simple to implement and easy to
use... :-)

Ciao, ET.

Reply | Threaded
Open this post in threaded view
|

Re: light userdata

Adrian Sietsma
In reply to this post by Wim Couwenberg-4
I find that lightuserdata is ideal for window handles (in the ms world).

I have a routine that constructs a gui object (tree view) from a lua table. I then have a seperate lua table indexed by the window handle of each tree node, as a lightuserdata. This simplifies the c/lua interface - i can use the c handle to lookup the appropriate lua table object.

--save a window handle
   lua_pushlightuserdata(L, hKey);
   lua_pushvalue(L,filt); // copy of filter table entry
   lua_settable(L, treeindex);


 --find data for a window handle
   lua_pushstring(L, "__treeitems");
   lua_gettable(L, LUA_GLOBALSINDEX);
   lua_pushlightuserdata(L, hKey);
   lua_gettable(L, -2);

In this case, the handle (lightuserdata) is never accessed by lua scripts, and so its type is irrelevant. This is a useful way of binding lua objects to c++ without worrying about pointers and the gc.

Adrian

Reply | Threaded
Open this post in threaded view
|

Re: light userdata

Mark Hamburg-4
Another example would be proxying C/C++/Objective-C objects to Lua. The
proxy is going to be a full userdata. (I keep wanting to call them "heavy
userdatas".) Furthermore, we'd like to share proxies if one already exists.
Solution: Use a weak table mapping light userdatas to full userdata proxies.

Roughly (and from memory)...

    pushObject (lua_State *L, Obj *obj )
    {
        lua_pushlightuserdata (L, &pushObject );
            /* Registry mapping for weak table. Key is arbitrary. */
        lua_rawget (L, LUA_REGISTRYINDEX);
        if( lua_isnil (L, -1) ) {
            lua_pop (L, 1);
            lua_newtable (L);
            lua_newtable (L);
            lua_pushliteral (L, "__mode");
            lua_pushliteral (L, "kv");
            lua_rawset (L, -3);
            lua_setmetatable (L,-2);
            lua_pushlightuserdata (L, &pushObject);
            lua_pushvalue (L, -2);  /* the table */
            lua_rawset (L, LUA_REGISTRYINDEX);
        }
        lua_pushlightuserdata (L, obj);
        lua_rawget (L, -2);
        if( lua_isnil( L, -1 ) ) {
            Obj **proxy;
            lua_pop (L, 1);
            proxy = (Obj**) lua_newuserdata (L, sizeof(*proxy));
            *proxy = obj;
            /* setmetatable on proxy */
            lua_pushlightuserdata (L, obj);
            lua_pushvalue (L, -2);
            lua_rawset (L, -4); /* weak table */
        }
        lua_remove (L, -2); /* weak table */
    }

This may actually be illegal since it relies on turning a function pointer
into a void* but the point is that we want some unique address to use as a
light userdata key. If you don't worry about independent Lua universes, you
can gain some speed by using a Lua ref to find the weak table.

Mark

P.S. This is the sort of code where lua_lock gets really, really
expensive...

on 7/26/04 2:47 PM, Adrian Sietsma at [hidden email] wrote:

> I find that lightuserdata is ideal for window handles (in the ms world).
> 
> I have a routine that constructs a gui object (tree view) from a lua
> table. I then have a seperate lua table indexed by the window handle of
> each tree node, as a lightuserdata. This simplifies the c/lua interface
> - i can use the c handle to lookup the appropriate lua table object.
> 
> --save a window handle
>   lua_pushlightuserdata(L, hKey);
>   lua_pushvalue(L,filt); // copy of filter table entry
>   lua_settable(L, treeindex);
> 
> 
> --find data for a window handle
>   lua_pushstring(L, "__treeitems");
>   lua_gettable(L, LUA_GLOBALSINDEX);
>   lua_pushlightuserdata(L, hKey);
>   lua_gettable(L, -2);
> 
> In this case, the handle (lightuserdata) is never accessed by lua
> scripts, and so its type is irrelevant. This is a useful way of binding
> lua objects to c++ without worrying about pointers and the gc.
> 
> Adrian


12