lua-users home
lua-l archive

[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