lua-users home
lua-l archive

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


Hi all,

I would like to be able to create a 'special' kind of table in Lua, which behind the scenes is bound to a C++ instance object. The C++ object (userdata or lightuserdata) itself should not be visible in Lua. The reason is that I need to trigger table-specific callback functions in Lua at deferred times determined by C++ code (after the main Lua chunk has run).

My needs are:
1. To be able to access the table from the C++ instance pointer (via callbacks in C API, but not in Lua code) 2. To be able to access the C++ instance pointer from the table (via metatable methods in C API, but not in Lua code).
	3. To have a callback into C++ when the table is garbage collected
4. To be efficient (minimal cpu use), and use a custom allocator for any C++ data (memory pool)

I have looked at using combinations of metatables, boxed pointers, userdata environments, the registry, weak keys, etc. but so far I can't find a solution that satisfies all the requirements.

For 1.
I can use the userdata environment if I don't use boxed pointers (set the userdata env to be the associated table) The problem with boxed pointers is that I can't get from the pointer that is boxed to the userdata object. I can use the registry, but have to automatically remove the userdata when the table is garbage collected

For 2.
I could use the registry, but have to be careful to remove the table when the instance is deleted. I could create a closure for each table that retrieves the userdata pointer from the upvalue; a kind of __getptr metamethod. This seems a little obscure.

For 3.	
It seems the only way I can do this is to use a userdata with a __gc method referenced by the table somewhere. Since I don't want the userdata to be available to Lua code, it seems the only place to do it is in the registry. REGISTRY[table] = udata. But actually I can't directly use the registry, since this will prevent the table ever getting collected; so I need to insert a weak-valued table of references into the registry and use that instead. REGISTRY [WEAKVALUEDREFS][table] = udata. Right?

For 4.
	This pretty much demands that I use boxed pointers.

So (in minimal pseudocode):

udata = lua_newuserdata()	// boxed or unboxed?
table = lua_newtable()

REGISTRY[WEAKVALUEDREFS][table] = udata
setfenv(udata, table) ?? // only for unboxed userdata

udata.__gc() { // do whatever gc callback here }

getUdata(table) {
	return REGISTRY[WEAKVALUEDREFS][table]
}

getTable(udata) {
	 ?? getfenv(udata) // only for unboxed userdata
}

Is there a way to achieve the same using a boxed pointer? Is there another way I haven't thought of yet? Is it a bad idea in terms of efficiency to use the registry for every object (I may have thousands)?