lua-users home
lua-l archive

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


I've implemented attributes with the following metamethods and setup code:

usr_attr_mt(
	lua_State *L,
	luaL_reg *methods,
	luaL_reg *getters,
	luaL_reg *setters
) {
	for(int i=0; methods[i].name; i++) {
		lua_pushcfunction(L, methods[i].func);
		lua_setfield(L, -2, methods[i].name);
	}
	
	lua_newtable(L);
	if(getters) {
		for(int i=0; getters[i].name; i++) {
			lua_pushcfunction(L, getters[i].func);
			lua_setfield(L, -2, getters[i].name);
		}
	}
	lua_setfield(L, -2, "__getters");
	

	lua_newtable(L);
	if(setters) {
		for(int i=0; setters[i].name; i++) {
			lua_pushcfunction(L, setters[i].func);
			lua_setfield(L, -2, setters[i].name);
		}
	}
	lua_setfield(L, -2, "__setters");
}

usr_attr_index(lua_State *L) {
	// first check metatable:
	lua_getmetatable(L, 1);
	lua_pushvalue(L, 2);
	lua_rawget(L, -2);
	if(lua_isnil(L, -1)) {
		lua_pop(L, 1);	// bad result; but leave metatable there...
		// next try __getters:
		lua_pushstring(L, "__getters");
		lua_rawget(L, -2);
		lua_pushvalue(L, 2);
		lua_rawget(L, -2);
		if(lua_type(L, -1) == LUA_TFUNCTION) {
			lua_pushvalue(L, 1);
			int err = lua_pcall(L, 1, 1, 0);
			if(err) {
				luaL_error(L, "error indexing %s.%s: %s\n",
										lua_tostring(L, 1),
										lua_tostring(L, 2),
										lua_tostring(L, -1));
			}
		} else {
			lua_pop(L, 3); // non-getter, __getters, metatable
			// try fenv
			lua_getfenv(L, 1);
			lua_pushvalue(L, 2);
			lua_rawget(L, -2);
		}
	}
	return 1;
}



usr_attr_newindex(lua_State *L) {

	// find setter:
	lua_getmetatable(L, 1);
	lua_pushstring(L, "__setters");
	lua_rawget(L, -2);
		lua_pushvalue(L, 2);
		lua_rawget(L, -2);
	// call setter:
	if(lua_type(L, -1) == LUA_TFUNCTION) {
		lua_pushvalue(L, 1);
		lua_pushvalue(L, 3);
		int err = lua_pcall(L, 2, 0, 0);
		if(err) {
			luaL_error(L, "error indexing %s.%s: %s\n",
										lua_tostring(L, 1),
										lua_tostring(L, 2),
										lua_tostring(L, -1));
		}
	} else {
		lua_pop(L, 1); // non-setter
	}
	lua_pop(L, 2); // __setters, metatable

	// store the value in the fenv:
	lua_getfenv(L, 1);
	lua_pushvalue(L, 2);
	lua_pushvalue(L, 3);
	lua_rawset(L, -3);
	lua_pop(L, 1);	// fenv
	return 0;
}