lua-users home
lua-l archive

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



On Feb 26, 2007, at 1:50 PM, Rici Lake wrote:

On 26-Feb-07, at 4:08 PM, Graham Wakefield wrote:

It depends on why you need to export the table to Lua. (Note:
I call this table the "peer table" below.) Often,
you can just export the userdata directly, and make the table
the userdata's environment; then the userdata's __index meta
points to the environment, and the environment's __index meta
points to your base instance-method table for the object type.
Unfortunately, that requires creating an individual metatable
for each userdata, in addition to the peer table; combining
the two is possible but somewhat less protective.

Yes, I've been trying this approach today; mapping __index and __newindex metamethods of the userdata to the userdata environment table. A disadvantage of this is that in Lua my object behaves as a table for general use, but not as a table for functions such as table.foreach() and generic for, which could be confusing for end users. Is there a way to implement a next() method for the generic for construction?

Sorry I didn't understand why there needs to be an individual metatable per instance? I'm interested in efficiency since I will have so many of these instances.

As to the why: I want users to be able to create a kind of table with user-defined values & functions; and then have callbacks occur at later times (via a C++ scheduler) on these tables, as part of a general multimedia performance tool.

Since a userdata's environment table is not garbage collected
until after the userdata's __gc metamethod is run, the __gc
metamethod can still access the peer table.

This is useful to know! Thanks.

In any event, if the translation from c++ pointer to userdata
is done in the context of a known lua_CFunction (or a small
set of them), you can keep the translation table (lightuserdata
to userdata) as an upvalue of the lua_CFunction(s), thus saving
a REGISTRY lookup. That makes translations almost twice as fast,
but you need to create all the closures of the lua_CFunctions
with the upvalue, which is not always convenient.

In fact in my case, there are probably less than 5 lua_CFunctions, so this is a very viable solution for the table->userdata lookup.

But the question is how I can get from a C++ pointer (after the main Lua chunk has run) to the Lua userdata. This is not a call from Lua to C++ triggered by a lua_CFunction, it is a call from C++ to Lua triggered by a C++ callback. How do I push the userdata onto the stack, from a C++ instance pointer? For this, I could not find any other solution than the REGISTRY:

REG[ptr] = udata	


If the C++ object can be destroyed when the last Lua
reference to it is gone, and if it has no destructor
(and none of its members have destructors), then you
could allocate it directly into a userdata with
placement new, and thereby avoid the __gc metamethod
altogether. That's only occasionally feasible, but it's
worth keeping in mind. Also, remember that with Lua 5.1,
you can provide a custom allocator to Lua, if that helps
you at all.

I just googled 'placement new', since I was not familiar with it; indeed this could be a great solution in my case. I still need the gc () method because the pointer still will be referenced in C++, but I can clear that reference in the gc() method.

So it would be something like:

// allocate:
MyClass * c = new(lua_newuserdata(L, sizeof(MyClass))) MyClass();

// and then on __gc():
c->~MyClass(); // this call must remove all other C++ references to the object c

If I do this, I presume that there is still no way to push the userdata on the stack using the MyClass * c pointer? It's a shame there is no lua_pushuserdata() function, but I do understand why there wouldn't be. I still need to add REG[ptr] = udata; where now ptr == c.



Surely there is a more efficient way to do this!?

Hope some of the above helped.


Yes indeed, thank you very much!  This list is always so helpful!