|
On Jul 3, 2012 6:24 PM, "Carsten Fuchs" <carsten.fuchs@cafu.de> wrote:
>
> Hi all,
>
> my preferred way of binding C++ objects to Lua has always been the direct and lightweight way as described in [1] and [2].
>
> In summary, from the mid section of [2], where C++ objects are represented in Lua by userdata:
>
> a) Prepare table to be used as metatable for each supported type, possibly with inheritance.
>
> b) Prepare an auxiliary table with weak values to find already existing userdata by lightuserdata (object pointer) later.
>
> c) For each C++ object for which a Lua "alter ego" is requested, find the existing userdata in the table from step b). If not found, create a new userdata, assign the proper metatable, and add an entry to table from step b).
>
>
> A key feature of this approach is that Lua determines the lifetime of the C++ object -- if Lua can garbage collect the related Lua userdata, the related __gc metamethod will naturally destroy the C++ object.
>
> Keeping a pointer to the C++ objects in the C++ code is thus not possible (it might get deleted in __gc), and especially we cannot hold the C++ object longer than the Lua state exists.
>
> One possible solution to this is to "anchor" the userdata in the Lua state, e.g. in another table with "strong" keys and values. However, I found such anchoring remarkably cumbersome to handle -- depending on the details, required management in the C++ code gets relatively complex. Anyways, this prevents our instance from being deleted via the garbage collector, but doesn't help with the lifetime beyond the Lua state.
>
> Eventually I figured that it might help to keep in userdata not a pointer to the C++ object, but a smart pointer, such as shared_ptr<>, intrusive_ptr<>, whatever.
>
> I've not yet tried it, but this seems to solve the problem very well:
> Neither C++ nor Lua forces the lifetime of the object onto each other, because both in the normal C++ code as well as in __gc only smart pointers expire, and the actual object only with the very last of them.
>
>
> Unfortunately, even this very appealing approach seems to have one weakness:
>
> Consider a windowing system, where R is the root window, we have the ability to add extra attributes to our Lua userdata (e.g. in an attached table), and do something like this:
>
> w = CreateNewWindow()
>
> -- Define the event handler in Lua.
> w._OnClick_ = function() ... end
>
> -- The implementation adds a smart pointer to w in R,
> -- but doesn't create another anchor for w in Lua anywhere.
> R.AddChildWindow(w)
>
> w = nil
>
> Now when w gets garbage collected, it won't be a problem for R, which in C++ code may keep a shared_ptr<> to w, but if we ever tried to call the OnClick() function of w, w would be recreated as described in step c) above, but OnClick() would not be there, because it was garbage collected with the original w earlier.
>
> So I was wondering if there was something like another callback metamethod for userdata, e.g. __mark(), that the garbage collector calls in order to decide if the userdata should be marked for collection.
> By default, it would return "true" of course, but in my case, I would w only want to be marked if all other smart pointers (those in C++) were gone already. That is, I'd want to return "true" only if the reference count of the C++ instance was 1 (by the Lua userdata smart pointer).
> This way, as long as the C++ object is in use in the C++ code, it's alter ego in Lua would be prevented from being garbage collected.
>
> Is it possible to have a callback like __mark somehow?
> (Or anything else that achieves a similar result?)
>
> Best regards,
> Carsten
>
>
> [1] Programming in Lua, 2nd edition
> [2] Game Programming Gems 6, chapter 4.2 "Binding C/C++ Objects to Lua"
>
>
>
> --
> Cafu - the open-source Game and Graphics Engine
> for multiplayer, cross-platform, real-time 3D Action
> Learn more at http://www.cafu.de
>
>
It sounds like you just need to keep references to your objects in the registry. They won't be collected as long as there's still a reference.
--
Sent from my microwave.