lua-users home
lua-l archive

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


On Sat, Nov 16, 2013 at 3:34 PM, Geoff Smith <spammealot1@live.co.uk> wrote:
> 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
>
> The code I posted I have tested and works for assorted C functions and the
> garbage collection, __tostring etc.
>
> But for the Lua side btn:action() function  I cant get a reference to it to
> call it via a pcall. That's the one thing that I am stuck on
>
> Your solution below didn't even mention an "action" string, so it ignored
> the bit I am stuck on.
>
> Also I didn't explain clearly but   pToObject  is a ptr to the button object
> in my C side graphics library, Lua doesn't need to know about that so
> pushing it on the stack cant be correct.
>
>  `pToUserdata`.  is the ptr to the btn1 userdata in my example
>
>>  but the name you've given it suggests a disturbing misunderstanding
>
> its a ptr to a userdata block for that specific instance, so I didn't see
> why you thought it implied a misunderstanding ?
>
>
> I could probably workaround this problem by forcing the user to do something
> like
>
> btn1:registerHandler(myHandlerFunc)
>
> 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.
>
> Regards Geoff
>
>
>
>
>
>
>
>
>
>> From: andrew.starks@trms.com
>> Date: Sat, 16 Nov 2013 14:29:19 -0600
>
>> To: lua-l@lists.lua.org
>> Subject: Re: Another userdata question
>>
>> On Sat, Nov 16, 2013 at 9:04 AM, Geoff Smith <spammealot1@live.co.uk>
>> wrote:
>> > If anyone can spare a bit of time to read the attached file and maybe
>> > fill
>> > in a few missing lines I would be most grateful.
>> > Is it possible to do without using Cclosures or lightuserdata ? No idea,
>> > but would be good if they could be avoided for reasons of simplicity. My
>> > attachment has been snipped down a bit to make it a smaller example
>> > file.
>>
>> Geoff,
>>
>> I took a look at your code.
>>
>> I think that the answer is pretty simple and that you are down a path
>> that is over complicated.
>>
>>
>> First, you need a place to stash your C pointer, so that you can use
>> it as a reference to locate your userdata. The normal way to do this
>> is to make a table in the registry. Let's call it
>> "GRAPHICS_BUTTON_PTRS"
>>
>> We need to make this a weak-keyed table, so that the reference doesn't
>> interfere with collection. We do this with a metatable `mt` that has:
>>
>> ```
>> mt.__mode = "v"
>> ```
>> I'll assume that you know how to make a table in the registry and you
>> know how to assign a metatable to that table.
>>
>>
>> So, given that you've created this table, you're almost there.
>>
>> In `udNewButton` you make the userdata `pToUserdata`. That's your
>> user data, but the name you've given it suggests a disturbing
>> misunderstanding about the nature of userdata. We'll get to that
>> later.
>>
>> Later on in that function, we get
>>
>> ```
>> objPtr = createObject(LIB_GRAPHICS_BUTTON, (char*)objectName); //
>> get a ptr to an unused button from the retired list and name this
>> button
>> ```
>> I'm going to assume that this is the pointer returned by the event
>> that you are concerned with.
>>
>> After this line, do something like this:
>>
>> ```
>> lua_getfield(L, LUA_REGISTRYINDEX, GRAPHICS_BUTTON_PTRS);
>> //there is now your weak table sitting on the stack.
>>
>> lua_pushlightuserdata (L, objPtr);
>> //now your pointer is on the stack.
>> lua_pushvalue(L, 8);
>> //Assuming that the userdata that you created earlier is at 8, this
>> will copy it to the top of the stack.
>>
>> lua_settable(L, -3)
>> //This means: In GRAPHICS_BUTTON_PTRS[objPtr] = pToUserdata
>>
>> -------
>>
>> So, much later, an event is fired, causing `buttonEventToLua(
>> OSLIB_OBJECT *pToObject)` to be called.
>>
>> Rewrite this function:
>>
>> ```
>> ///UNTESTED!!
>>
>> void buttonEventToLua( OSLIB_OBJECT *pToObject)
>>
>> {
>>
>> lua_State *local_L = L_Store;
>>
>> lua_getfield(L, LUA_REGISTRYINDEX, GRAPHICS_BUTTON_PTRS);
>> lua_pushlightuserdata (L, pToObject);
>> lua_gettable(L, -2);
>> if(lua_isnil(L,-1))
>> lua_error("Pointer not found!");
>>
>> ////your userdata is now at the top of the stack.
>>
>> ////do stuff.....
>>
>> }
>>
>> ````
>>
>> Does this make sense?
>>
>>
>> Here is something that's important to really understand:
>>
>> Userdata is Lua's. You don't own it at all. Once you make it, assume
>> that Lua can destroy it at any time. This means that anything that
>> you've alloced in that object needs to be destroyed from that UD's
>> __gc metamethod and you can't be holding on to it anywhere else,
>> within lua. (or if you are, you need to manage it)
>>
>> You can't "push" userdata on the stack and you can't store a pointer
>> to it, somewhere else. It's Lua's ONLY.
>>
>> Once it is on the stack, you can move/copy/insert it from one position
>> to another. To get it on the stack, it needs to be there in a function
>> call from Lua OR you need to retrieve it from a known location.
>>
>> Lightuserdata and the registry can be helpful here. Lightuserdata is a
>> void * and as such, it can only be equal to itself. This happens to
>> line up nicely with libraries that pass pointers in, which identify
>> the source of an event. We can use this pointer, which can only be
>> equal to itself, as the key value in a table that maps to a stashed
>> userdata value.
>>
>> And then, BANG! You've got exactly what you need to respond to the event!
>>
>> -Andrew
>>

Well, I think that my example was necessary to get yo 90% there.

The remaining 10% is simple:

In your userdata object, create a uservalue with:
lua_newtable(L); lua_netable(L);
lua_setfield(L, -2, "callbackfunctions");
//your user can add functions to this table. You may want to put dummy
functions in there, now as well.

lua_setuservalue(L, 8); //position 8 is where your user value was.

Now, in your callback function, in addition to retrieving the userdata:

lua_getuservalue(L,-1);
now you've got the table. Now you can call your named lua functions from C.

If I have the question correctly understood, is the rest clear?

-Andrew