lua-users home
lua-l archive

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


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