checking userdata type from lua

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

checking userdata type from lua

Daniel Collins
For my current project I have made lua bindings for a UI system. The
bulk of this consists of userdata types for panels, buttons, checkboxes
and other widgets, plus a bunch of factory methods. In order to simplify
menu creation and use, I am also creating a set of utility lua objects
that add functionality to the user data types. Which brings me to my
question.

In the C methods operating on the userdata, I can use luaL_checkudata to
verify that the userdata is the correct type. Is there any analogous way
to do this on the lua side? Just doing 'type(object) == "userdata"' is
not sufficient, since that doesnt distinguish a Button from any other
userdata type.

I can quite easily add "IsButton(x)" methods to the menu object, one for
each of the factory methods and implement them in C using
luaL_checkudata. I was just wondering if there is some simpler way first
to do this entirely in lua.

Do people generally do things like this to confirm that they have the
correct userdata type? Or do you just rely on the errors that arise when
non-existant table entries are accessed?

Maybe I am just having trouble letting go of my need for strong typing
:)

- DC

Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Alex Queiroz
Hallo,

On 5/18/06, Daniel Collins <[hidden email]> wrote:
> In the C methods operating on the userdata, I can use luaL_checkudata to
> verify that the userdata is the correct type. Is there any analogous way
> to do this on the lua side? Just doing 'type(object) == "userdata"' is
> not sufficient, since that doesnt distinguish a Button from any other
> userdata type.
>

     You can do what luaL_checkudata does: check if the object's
metatable is equal to the metatable of the "type".

--
-alex
http://www.ventonegro.org/
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Daniel Collins
In reply to this post by Daniel Collins
>      You can do what luaL_checkudata does: check if the
> object's metatable is equal to the metatable of the "type".

Yes, but the metatable for the type is stored in the registry and I
thought that was only accessible from the C API.
So in lua I can get the metatable for the object I want to test with
getmetatable, but I cant see a way to get the metatable for the type
from the registry in order to do the comparison.

I guess it is not a major issue since I can implement something based on
luaL_checkudata pretty simply. I just didn't want to fall into the habit
of writing C API functions for things that can actually be done in lua
itself.

- DC

Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Alex Queiroz
Hallo,

On 5/18/06, Daniel Collins <[hidden email]> wrote:
> >      You can do what luaL_checkudata does: check if the
> > object's metatable is equal to the metatable of the "type".
>
> Yes, but the metatable for the type is stored in the registry and I
> thought that was only accessible from the C API.

     Sure, you'd have to make it accessible from Lua as well, maybe in
a global table.

--
-alex
http://www.ventonegro.org/
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Luiz Henrique de Figueiredo
In reply to this post by Daniel Collins
> Or do you just rely on the errors that arise when non-existant table
> entries are accessed?

That's what I do in my libraries. I have never felt the need to test
the type of udata in Lua. There's not much you can with udata in Lua,
except calling methods on them, and then you'll have type checking
done in C (or at least somewhere down the road).

If you really need to check types in Lua, you can redefine "type" in
Lua itself to look into special fields or tables. For instance, you
could create a key-weak table that maps udata to types and update it
every time you create a udata object.
--lhf
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Luiz Henrique de Figueiredo
In reply to this post by Alex Queiroz
> > Yes, but the metatable for the type is stored in the registry and I
> > thought that was only accessible from the C API.
>
>      Sure, you'd have to make it accessible from Lua as well, maybe in
> a global table.

That's dangerous. If you expose the registry, you can turn a udata from
one type to another. So you could convert a button to a file and then
call close on it, which will probably crash the host app.
--lhf
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Roberto Ierusalimschy
In reply to this post by Daniel Collins
> I guess it is not a major issue since I can implement something based on
> luaL_checkudata pretty simply. I just didn't want to fall into the habit
> of writing C API functions for things that can actually be done in lua
> itself.

As userdata is C stuff, it is better to make C handle it. That is how
the standard library does it (see io.type).

-- Roberto
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Javier Guerra Giraldez
In reply to this post by Luiz Henrique de Figueiredo
On Thursday 18 May 2006 7:58 am, Luiz Henrique de Figueiredo wrote:
> >      Sure, you'd have to make it accessible from Lua as well, maybe in
> > a global table.
>
> That's dangerous. If you expose the registry, you can turn a udata from
> one type to another. So you could convert a button to a file and then
> call close on it, which will probably crash the host app.

isn't this already possible with setmetatable() ?


--
Javier

attachment0 (205 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Daniel Collins
In reply to this post by Daniel Collins
> > Or do you just rely on the errors that arise when
> non-existant table
> > entries are accessed?
>
> That's what I do in my libraries. I have never felt the need
> to test the type of udata in Lua.

That's a very polite way of suggesting I let go of the desire for strong
types :-)

And you are correct, there is not much I can do with the udata in lua
apart from call C methods, and they already do check the type.

If at some point I do really need udata type checking in lua, I will
follow Roberto's suggestion and implement something like io.type.


- DC

Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Luiz Henrique de Figueiredo
In reply to this post by Javier Guerra Giraldez
> > That's dangerous. If you expose the registry, you can turn a udata from
> > one type to another. So you could convert a button to a file and then
> > call close on it, which will probably crash the host app.
>
> isn't this already possible with setmetatable() ?

No, try it! From Lua you can only set the metatables of tables, not of
udata, precisely to avoid crashing the host app from Lua.

Lua is secure, unless *you* make it insecure...
--lhf
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Roberto Ierusalimschy
In reply to this post by Javier Guerra Giraldez
> isn't this already possible with setmetatable() ?

>From the manual:

  setmetatable (table, metatable)

  Sets the metatable for the given table.
  (You cannot change the metatable of other types from Lua, only from C.)


-- Roberto
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Javier Guerra Giraldez
In reply to this post by Luiz Henrique de Figueiredo
On Thursday 18 May 2006 8:23 am, Luiz Henrique de Figueiredo wrote:
> > > That's dangerous. If you expose the registry, you can turn a udata from
> > > one type to another. So you could convert a button to a file and then
> > > call close on it, which will probably crash the host app.
> >
> > isn't this already possible with setmetatable() ?
>
> No, try it! From Lua you can only set the metatables of tables, not of
> udata, precisely to avoid crashing the host app from Lua.

great!  and i see getmetatable() does work, making it possible to check the
type.


--
Javier

attachment0 (205 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Petite Abeille
In reply to this post by Roberto Ierusalimschy

On May 18, 2006, at 15:23, Roberto Ierusalimschy wrote:

>   (You cannot change the metatable of other types from Lua, only from
> C.)

What about debug.setmetatable (object, table)?

debug.setmetatable( 1, { __index = math } )

print( ( -1 ):abs() )

 > 1

Cheers

--
PA, Onnay Equitursay
http://alt.textdrive.com/

Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Roberto Ierusalimschy
> What about debug.setmetatable (object, table)?

>From the manual:

  5.9 - The Debug Library

  This library provides the functionality of the debug interface to Lua
  programs. You should exert care when using this library. The functions
  provided here should be used exclusively for debugging and similar
  tasks, such as profiling. Please resist the temptation to use them as a
  usual programming tool: They can be very slow. Moreover, several of its
  functions violate some assumptions about Lua code (e.g., that variables
  local to a function cannot be accessed from outside or that userdata
  metatables cannot be changed by Lua code) and therefore can compromise
  otherwise secure code.

-- Roberto
Reply | Threaded
Open this post in threaded view
|

Re: checking userdata type from lua

Daniel Collins
In reply to this post by Daniel Collins
> > No, try it! From Lua you can only set the metatables of
> tables, not of
> > udata, precisely to avoid crashing the host app from Lua.
>
> great!  and i see getmetatable() does work, making it
> possible to check the
> type.

Using getmetatable was my first thought as well. But that is only half
the story. The check in luaL_checkudata gets both the metatable from the
udata and the metatable from the registry for the specified type. Then
it checks if they are the same. So in lua I can only get the udata
metatable but not the other table to compare against.

It doesn't matter anyway, I have decided not to bother checking the
udata type in lua. There is almost nothing I can do with the wrong type
that wont produce an error of some sort already.

- DC

Reply | Threaded
Open this post in threaded view
|

Re: storing a lua closure in a C object, then calling it from C?

Graham Wakefield
In reply to this post by Javier Guerra Giraldez
Hi,

I'm relatively new to Lua, and I've hit a conceptual block about  
which I'd really appreciate some more experienced input.

I want to create objects in Lua that are tied to some C data  
structure (created from my own allocated memory pool), but also to  
user-defined Lua functions.  At a later point, I will need to iterate  
through my allocated C objects and call the associated Lua functions  
in turn (along with associated upvalues, and passing in the C data  
structure as an argument).  I'm looking for the most efficient  
solution to this problem, scalable to hundreds (or more) such  
objects.  All calls will be made in a determinate order, so there  
should be no thread-unsafe issues.  The objects shouldn't be  
allocated or gc'd by Lua, since I'm using a memory pool, but some  
kind of equivalent gc notification to trigger recycling might be  
useful on either the Lua or C side.

I understand (I think) the difference between light and full  
userdata, but I haven't figured out yet how I could associate a  
reference to a Lua function into my self-allocated C data structure.  
I've seen examples of storing function references as strings, but  
that seems both unsafe and inefficient for my purposes (and prevents  
storing anonymous functions/closures).  I can see how to push and  
read C functions from the Lua stack, but not Lua functions/closures.  
I also read something about the registry (but haven't totally  
understood it yet) - would that help?  Could I use a light userdata  
as a key in the registry?

Or is there a simpler method I'm overlooking?

Thanks in advance!

Graham


Reply | Threaded
Open this post in threaded view
|

RE: storing a lua closure in a C object, then calling it from C?

Daniel Collins
It is not quite the same situation but the UI system I have been
developing this last week does something similar.

In my C code I first create two lua tables: one for storing callback
functions and the other for storing the full userdata.

When the lua code calls a factory method to create a new widget my C
factory function ends up with 3 essential items - the C object that
represents the UI widget in the C API, the full userdata representing
the widget for lua, and on the stack there is a lua function that was
passed as a parameter to the factory method. I add the callback function
to its table and the userdata to its table using the C object pointer
(pushed as lightuserdata) as the key in both cases.

Then when my C API triggers a UI event for one of the widgets, I get a
function call with a pointer to the C object. This then looks up the
function (or closure) and puts it on the stack. Second it looks up the
fulluserdata, puts it on the stack then does a lua_call (this is already
running in protected mode so I don't need a pcall). This works really
well. Passing the fulluserdata as the first parameter means the callback
is compatible with lua methods (eg: function m:callback()).

This is also similar to your case since the actual widgets are allocated
outside of lua. The fulluserdata just holds the C pointer. The fact that
I am holding the fulluserdata in a lua table prevents any garbage
collection.

Since my UI system also has a C API for creating widgets, it also has a
pathway for storing cfunctions in the callback table. In this case the
callback function receives the C pointer rather than the fulluserdata as
the only parameter.

I could also have used a single table, keyed by the C pointer with each
entry being another table holding the callback function and the
fulluserdata but I don't see how this would be any different for my
usage pattern. Perhaps if I was updating all the objects.

The fact that some of the registered functions have upvalues and others
don't is all handled transparently since I am using lua tables and API
calls to handle all the data.

- DC

 

Reply | Threaded
Open this post in threaded view
|

Re: storing a lua closure in a C object, then calling it from C?

Daniel Collins
In reply to this post by Graham Wakefield
> In my C code I first create two lua tables: one for storing
> callback functions and the other for storing the full userdata.

I forgot to mention, these are stored in the registry. I could have
stored them in an environment except that I also manage callbacks to C
functions with this system. The C function callbacks are created in
functions that are not registered with Lua at all. I didn't want to
register these functions simply so they could access the environment.

- DC

Reply | Threaded
Open this post in threaded view
|

Re: storing a lua closure in a C object, then calling it from C?

Graham Wakefield
In reply to this post by Daniel Collins
Hi,

Thanks for the detailed reply!

On May 21, 2006, at 2:56 AM, Daniel Collins wrote:
>
> In my C code I first create two lua tables: one for storing callback
> functions and the other for storing the full userdata.

I thought I might have to something like this.  I'm still not sure  
about the difference between the registry and the environment, but  
I'm new to Lua and will keep on reading the manual...

> When the lua code calls a factory method to create a new widget my C
> factory function ends up with 3 essential items - the C object that
> represents the UI widget in the C API, the full userdata representing
> the widget for lua, and on the stack there is a lua function that was
> passed as a parameter to the factory method. I add the callback  
> function
> to its table using the C object pointer (pushed as lightuserdata)  
> as the key

What calls do you use to grab the function and push it into the  
table?  lua_tothread() and lua_pushthread() ?  I can't see any other  
API functions to grab a Lua function from the stack, or place one on  
the stack...
Or do you use lua_rawget() and lua_setfield() ?

> Then when my C API triggers a UI event for one of the widgets, I get a
> function call with a pointer to the C object. This then looks up the
> function (or closure) and puts it on the stack. Second it looks up the
> fulluserdata, puts it on the stack then does a lua_call (this is  
> already
> running in protected mode so I don't need a pcall). This works really
> well. Passing the fulluserdata as the first parameter means the  
> callback
> is compatible with lua methods (eg: function m:callback()).

Nice!

> This is also similar to your case since the actual widgets are  
> allocated
> outside of lua. The fulluserdata just holds the C pointer. The fact  
> that
> I am holding the fulluserdata in a lua table prevents any garbage
> collection.
>
> Since my UI system also has a C API for creating widgets, it also  
> has a
> pathway for storing cfunctions in the callback table. In this case the
> callback function receives the C pointer rather than the  
> fulluserdata as
> the only parameter.
>
> I could also have used a single table, keyed by the C pointer with  
> each
> entry being another table holding the callback function and the
> fulluserdata but I don't see how this would be any different for my
> usage pattern. Perhaps if I was updating all the objects.

Or storing more than one callback function per object?

>
> The fact that some of the registered functions have upvalues and  
> others
> don't is all handled transparently since I am using lua tables and API
> calls to handle all the data.

Very nice.

>
> - DC
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: storing a lua closure in a C object, then calling it from C?

Jerome Vuarand
> I thought I might have to something like this.  I'm still not sure
> about the difference between the registry and the environment, but
> I'm new to Lua and will keep on reading the manual...

I'll try to give an explanation, but I may be wrong on some points
(real gurus should react if that's the case).

Both environment and registry are standard lua tables.

The registry is a global table that is normally only accessible
through C API. There is only one per Lua interpreter.

There may be several environment tables. The first outmost global
chunk creates the first global environment. It is then passed to all
subsequent created chunks (loaded lua files and created functions), so
that any chunk has a reference to an environment.You can change that
reference to point to another table, which becomes a new environment.
Initial chunk to environment linking is done at creation of chunk, not
at execution (that means at file loading, or function parsing). That's
the lexical scoping.

The problem with environments is that if you are in a C code that is
not called from Lua in any way, you are in no lexical scope and thus
you don't have a current environment (because no chunk is being
executed), and no access to any global variable, so you have to use
the registry to access stuff in Lua.
12