[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: set a Lua table as metatable of a userdata?
- From: "lists@..." <lists@...>
- Date: Wed, 16 Aug 2006 15:47:35 -0700
Hi,
Right - thanks.  I just did the same thing (trying it in Lua first  
before C), which helped a great deal to understand this. Also I had a  
bug in my newInstance, the userdata & table were the wrong way around  
on the stack for lua_setmetatable. It's working nicely I think now,  
though I noticed that the __gc method is only being called if I  
install it in the instance table (mt1), not the class table (mt2) as  
below.
It currently looks something like this (where mt1 = instance table,  
mt2 = class table):
static int newTest(lua_State * L)
{
	lua_newtable(L);					// mt1
	
	// push a temporary variable into the imt
	lua_pushstring(L, "instancevar");
	lua_pushnumber(L, 0.5);
	lua_settable(L, -3);
	
	// set mt1.__index = mt1
	lua_pushstring(L, "__index");
	lua_pushvalue(L, -2);
	lua_settable(L, -3);
	
	// set mt1.__newindex = mt1
	lua_pushstring(L, "__newindex");
	lua_pushvalue(L, -2);
	lua_settable(L, -3);
	
	// get class metatable (mt2) & assign to instance metatable (mt1)
	luaL_getmetatable(L, classname);
	lua_setmetatable(L, -2);		// setmetatable(mt1, mt2)
		
	// create the full userdata pointer
	void ** udata = (void **)lua_newuserdata (L, sizeof(void **)); //  
it's now on the stack
	*udata = new Test();			// have the fulluserdata point to the C++  
instance
	
	// move the pointer to stack index 1 & assign the input table to be  
the metatable of the userdata
	lua_insert(L, 1);
	lua_setmetatable(L, -2);		// setmetatable(udata, mt1)
	// now return the userdatum
	return 1;
}
The class registration looks something like this:
/*
	Register a class
*/
static int registerClass(lua_State * L, const char * classname, const  
luaL_Reg *methods, lua_CFunction newfn, lua_CFunction deletefn)
{
	// create class metatable (only visible to C API)
	luaL_newmetatable(L, classname);	// mt2
	
	// install methods
	luaL_openlib(L, NULL, methods, 0);	/* NULL means push the methods  
into the metatable */
	
	// mt2.__index = mt2
	lua_pushstring(L, "__index");
	lua_pushvalue(L, -2);	
	lua_settable(L, -3);
	// push a temporary variable into the imt
	lua_pushstring(L, "classvar");
	lua_pushnumber(L, 0.25);
	lua_settable(L, -3);
	// note: mt2.__newindex is not changed
	
	// install destructor
	lua_pushstring(L, "__gc");	
	lua_pushcfunction(L, deletefn);
	lua_settable(L, -3);				/* metatable.__gc = deletefn */
	
	// create constructor
	lua_pushcfunction(L, newfn);
	lua_setglobal(L, classname);
	
	return 1;	// default luaopen_ behaviour is to leave the metatable on  
the stack
}
Some test code in Lua:
a = NumArray()
b = NumArray()
print("a", a)
print("b", b)
a.temp = 1
b.temp = 2
print("a.temp", a.temp)
print("b.temp", b.temp)
print("a.instancevar", a.instancevar)
print("b.instancevar", b.instancevar)
a.instancevar = 3
print("a.instancevar", a.instancevar)
print("b.instancevar", b.instancevar)
print("a.classvar", a.classvar)
print("b.classvar", b.classvar)
-- this will not change classvar, it will override it with an  
instance variable
b.classvar = 4
print("a.classvar", a.classvar)
print("b.classvar", b.classvar)
Output:
a	userdata: 0x308c04
b	userdata: 0x308cb4
a.temp	1
b.temp	2
a.instancevar	0.5
b.instancevar	0.5
a.instancevar	3
b.instancevar	0.5
a.classvar	0.25
b.classvar	0.25
a.classvar	0.25
b.classvar	4