[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: use cases of getuservalue and setuservalue?
- From: Sean Conner <sean@...>
- Date: Tue, 27 Sep 2022 02:23:57 -0400
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