[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Another userdata question
- From: Sean Conner <sean@...>
- Date: Sat, 16 Nov 2013 19:38:35 -0500
It was thus said that the Great Geoff Smith once stated:
> Hello Andrew
>
> Thanks again for trying to assist, I think we just proved its tricky to
> formulate a clear question on a mailing list and to understand a partially
> complete piece of code out of context.
>
> Let me try to clarify the question again
>
> I want to be able to call a fixed name Lua function on my btn userdata
> instance.
>
> btn1 = newButton(x,y, etc)
>
> function btn1:action()
> print("this button was pressed")
> end
>
> I think I know how to code this alternate technique, but having a fixed
> btn1:action function seemed a better idea and saves the user having to
> register a function when the button is created.
>
> And anyway this is bugging the heck out of me now and I want to solve it !
> :) If I ever figure it out I will write a web page up on it. The technique
> to call a fixed name Lua defined function on a userdata instance is
> currently sadly lacking in all of the Lua literature out there.
I took a stab at it, using your original code as a base. I think this
will work, but this is untested code. It's commented so you should be able
to figure out what I did. Please don't hesitate to ask any questions.
-spc
static lua_State *L_Store;
/***************************************************************
* Call a fixed Lua function based on the object. There's a reference to the
* Lua based object in the Lua registry (see comment in buttonlua_new()). We
* then get the "action" field (which should be a function); if it doesn't
* exist, we just exit (no action, no foul). Otherwise, we call the function
* with the Lua based object as a parameter.
****************************************************************/
void button_event_to_lua(OSLIB_OBJECT *const obj)
{
lua_pushlightuserdata(L_Store,obj);
lua_gettable(L,LUA_REGISTRY);
lua_getfield(L,-1,"action");
if (lua_isnil(L,-1))
{
lua_pop(L,2);
return
}
lua_pushvalue(L,-2);
lua_call(L,1,0);
lua_pop(L,1);
}
/************************************************************************/
static int buttonlua___tostring(lua_State *const L)
{
/*-------------------------------------------------------------------------
; luaL_checkudata() will either never return (if the userdata isn't of the
; right type---it throws an error) or will always return and never return
; NULL. So there's no reason to check the result from luaL_checkudata().
;-------------------------------------------------------------------------*/
lua_pushfstring(
L,
"button:%p",
(void *)luaL_checkudata(L,1,GRAPHICS_BUTTON_TYPE)
);
return 1;
}
/****************************************************************************
* Lua has determined that this is garbage, and is calling our finalizer. We
* destroy the reference to our data in the Lua registry, then destroy our
* object.
*
* Ohh, I just thought of something---is the registry keep weak references?
* If not, then Lua will never consider this for garbage collection, and
* we're responsible for destroying it. Hmm ... we might have to create our
* own reference table with weak values, but I'll leave that up as an
* exercise for the reader.
****************************************************************************/
static int buttonlua___gc(lua_State *const L)
{
OSLIB_OBJECT **button = luaL_checkudata(L,1,GRAPHICS_BUTTON_TYPE);
lua_pushlightuserdata(L,*button);
lua_pusnnil(L);
lua_settable(L,LUA_REGISTRY);
destoryObjectByPtr(*button);
return 0;
}
/****************************************************************************/
static int buttonlua___index(lua_State *L)
{
lua_getfenv(L,1);
lua_replace(L,1);
lua_gettable(L,1);
return 1;
}
/****************************************************************************/
static int buttonlua___newindex(lua_State *const L)
{
lua_getfenv(L,1);
lua_replace(L,1);
lua_settable(L,1);
return 0;
}
/**************************************************************************/
static int buttonlua_show(lua_State *L)
{
OSLIB_OBJECT **button = luaL_checkudata(L,1,GRAPHICS_BUTTON_TYPE);
OSLIB_InvalidateObject(*button,1);
OSLIB_ShowObject(*button,1);
return 0;
}
/**************************************************************************/
static const luaL_Reg MapMethods[] =
{
{ "show" , buttonlua_show } ,
{ NULL , NULL }
};
static int buttonlua_new(Lua_State *L)
{
int x;
int y;
int width;
int height;
int radius;
const char *text;
const char *name;
OSLIB_OBJECT **button;
/*-------------------------------------------------------------------
; there is no need to check the number of arguments---the various
; luaL_check*() functions will error out if any of the arguments are
; missing.
;--------------------------------------------------------------------*/
name = luaL_checkstring(L,1);
x = luaL_checkint(L,2);
y = luaL_checkint(L,3);
width = luaL_checkint(L,4);
ht = luaL_checkint(L,5);
radius = luaL_checkint(L,6);
text = luaL_checkstring(L,7);
button = lua_newuserdata(L,sizeof(OSLIB_OBJECT));
memset(button,0,sizeof(OSLIB_OBJECT));
*button = createObject(LIB_GRAPHICS_BUTTON,name);
/*-------------------------------------------------------------------
; If the button couldn't be created, return nil. Since there's no
; reference to the userdata still on the stack, it will be reclaimed in
; the next garbage collection cycle, so we don't worry about it.
;----------------------------------------------------------------------*/
if (*button == NULL)
{
lua_pushnil(L);
return 1;
}
/*----------------------------------------------------------------------
; push a reference to our userdata into the Lua registry. This way, we
; can find it based on the object pointer the rest of the system uses.
;----------------------------------------------------------------------*/
lua_pushlightuserdata(L,*button);
lua_pushvalue(L,-2);
lua_settable(L,LUA_REGISTRY);
/*-----------------------------------------------------------------
; create an environment for our userdata to store arbitrary Lua values (so
; we can treat our userdata as a Lua table). Since the __index and
; __newindex methods refer to this table, we need to store non-metamethods
; here, so prepopulate with some functions.
;---------------------------------------------------------------*/
lua_createtable(L,0,0);
luaL_register(L,NULL,MapMethods);
lua_setfenv(L,-2);
/*-------------------------------------
; initialize our object
;--------------------------------------*/
OSLIBx_SetTextUtf8(*button,buttonText);
(*button)->position.x = x;
(*button)->position.y = y;
(*button)->size.cx = width;
(*button)->size.cy = ht;
(*button)->radius = radius;
luaL_getmetatable(L,GRAPHICS_BUTTON_TYPE);
lua_setmetatable(L,-2);
return 1;
}
/**************************************************************************/
static const luaL_Reg MetaMap[] =
{
{ "__tostring" , buttonlua___tostring } ,
{ "__gc" , buttonlua___gc } ,
{ "__index" , buttonlua___index } ,
{ "__newindex" , buttonlua___newindex } ,
{ NULL , NULL }
};
static const luaL_Reg Map[] =
{
{ "newButton" , buttonlua_new } ,
{ NULL , NULL }
};
LUALIB_API int luaopen_BookButton(lua_State *L)
{
L_Store = L;
luaL_newmetatable(L,GRAPHICS_BUTTON_TYPE);
luaL_register(L,NULL,MetaMap);
luaL_register(L,"graphics",Map);
return 1;
}