lua-users home
lua-l archive

Re: use cases of getuservalue and setuservalue?

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


It was thus said that the Great ubq323 once stated:
> what is the purpose of the lua_setuservalue and lua_getuservalue
> (or in lua 5.4, getiuservalue and setiuservalue) functions?
> what's the intended use case of being able to associate arbitrary lua
> values with full userdata?
> and does anyone know of any notable or interesting uses of this
> functionality?
 Yes.
 I have an experiemental Xlib wrapper for Lua that I work on from time to
time. There are several major data structures that Xlib requires to work,
one such structure is a graphical context, GC. It's used to manage things
like foreground color, background color, font, etc. To set the foreground,
one would do (in C):
	Display *gdisplay; /* set somewhere in code */
	GC gc; /* some graphics context */
	unsigned long color; /* a 32-bit value */
	XSetForeground(gdisplay,gc,mycolor);
 So it's straightforward enough to create a GC and call it from Lua. 
First, a sample metatable:
	static const luaL_Reg xgc_meta[] =
	{
	 { "__gc" , xgc___gc } ,
	 { "__index" , /* will be set to this table */ },
	 { "background" , xgc_background } ,
	 { "dashes" , xgc_dashes } ,
	 { "font" , xgc_font } ,
	 { "foreground" , xgc_foreground } ,
	 { "lineattributes" , xgc_lineattributes } ,
	 { "set" , xgc_set } ,
	 { NULL , NULL }
	};
 Now, I would like to be able to do:
	somegc = display:defaultGC()
	somegc:background('red')
	somegc:foreground('black')
 Here's the issue---the XSetForeground() and XSetBackground() need the
display variable to work. I could this by:
	somegc:background(display,'red')
but that gets tiresome, and besides, we got the somegc *from the
display*---passing in some other display will most likely not work at all.
But where to store the display? I don't want to make it a global variable
because there are instances where an X program can use multiple displays. 
So I would like to associate the display with the GC (or window, or font,
etc.). I don't want to store it in the metatable because, again, multiple
graphic contexts can exist, with different displays. I can't use a closure
for each function, because again, same reason. There's not much left, and
that really only leaves lua_setiuservalue() and lua_getiuservalue(), where I
can use to stash the display associated with the GC (or window, or other
structures that Xlib uses). Then, the code that wraps, say,
XSetForeground() will be:
	static int xlib_xsetforeground(lua_State *L)
	{
	 GC *gc = luaL_checkuserdata(L,1,TYPE_XLIB_GC);
	 lua_getiuservalue(L,1,1);
	 Display **display = luaL_checkudata(L,-1,TYPE_XLIB_DISPLAY);
	 XSetForeground(*display,*gc,xlibL_tocolor(L,2);
	 return 0;
	}
 It can also be extended to allow the user to store other data from Lua
that she might want associated with the GC by setting __index and __newindex
to functions that use, say, uservalue slot 2 to store a table:
	static int xlib___newindex(lua_State *L)
	{
	 lua_getiuservalue(L,1,2);
	 lua_replace(L,-4);
	 lua_settable(L,-3);
	 return 0;
	}
	
	static int xlib___index(lua_State *L)
	{
	 /*--------------------------------------------------------------
	 ; first we check the metatable for things like 'foreground' and
	 ; 'background' so we can call these functions on the userdata.
	 ;---------------------------------------------------------------*/
	 lua_getmetatable(L,1); /* allow us to check for existing functions */
	 lua_pushvalue(L,-2);
	 lua_gettable(L,-2);
	 
	 /*-------------------------------------------------------
	 ; If result is nil, check the user supplied data table
	 ;--------------------------------------------------------*/
	 
	 if (lua_isnil(L,-1))
	 {
	 lua_pop(L,2);
	 lua_getiuservalue(L,1,2);
	 lua_pushvalue(L,-2);
	 lua_gettable(L,-2);
	 }
	 return 1;
	}
 That's how I use lua_getiuservalue() and lua_setiuservalue().
 
 -spc

AltStyle によって変換されたページ (->オリジナル) /