[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Prevent userdata from being marked for garbage collection?
- From: Carsten Fuchs <carsten.fuchs@...>
- Date: Wed, 04 Jul 2012 00:24:34 +0200
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