views:

534

answers:

2

OK so I have a C++ class that is exposed to Lua using SWIG. The script creates the object but a manager class also has a pointer to the object so it can be modified in C++(or another script) for whatever reason.

The problem is that when the script finishes the object is freed, how can I control what the Garbage collector collects without having to implement a gc metamethod?

Here is an example:

--Script that creates the object
someObject = Utils.Object("Obj name");

Now the Object has registered itself with the manager so the rest of the application(and other scripts) can access it.

--Another script
obj = ObjManager:GetObject(0);

Clearly not a very realistic example but hopefully it illustrates my question. Is there a way to veto the garbage collector without a gc metamethod in C++?

Just to clarify the manager is in C++, and Utils is the module name housing the exposed class. Also the object registers itself to the manager in its constructor.

Thanks in advance.

A: 

Yes; have Utils.Object stuff the object in a private table. Then it will never be collected, but you can play games (code not tested):

do
  local retained = { }  -- table forces objects to be retained
  local old_util_object = Util.Object
  Util.Object = function(...)
    local obj = old_util_object(...)
    retained[obj] = true
    return obj
  end
  Util.Free = function(obj)
    assert(retained[obj])
    retained[obj] = nil  -- now obj can be garbage-collected
  end
end

If you want to solve the same problem on the C++ side, have your C++ code allocate a private table and put it in the Lua registry. Then you can play the same insertion/deletion game, only using the C API instead of Lua source. Assuming you have any familiarity with Lua's C API, it's straightforward. If you haven't used the C API before, there will never be a better time to start learning.

Norman Ramsey
Thanks for the answer but it doesn't really help. I was looking for a way to do it in C/++. I clarified my question to show that.
M_D_K
+2  A: 

Lua's GC only knows about references held within Lua, which is a reasonable implementation constraint. This implies that an object's lifetime is under Lua's control. If an object created by executing one script or function needs to be available to later scripts or functions, a reference to it has to be preserved within the Lua state so that the GC knows it is still in use. Otherwise, it is indistinguishable from garbage, and may be discarded at any time.

This one of the purposes of the Lua registry table. The C side can easily hold a reference to any Lua object by placing it in the registry table. The key can either be some unique value known to the C library (the address of a static variable converted to a light userdata is often a good choice since it cannot collide with any key from any other library). Alternatively the function call luaL_ref(L, LUA_REGISTRYINDEX) will put the item at the top of stack in the registry table and return a unique integer key. This works well for storing a script-provided callback function in a way that both protects the function from the GC and allows a "pointer" (the integer key) to it to be stored in a C structure so that it can be retrieved and called later.

Note that luaL_ref() can be used to manage references in any table, so it may very well make sense to use a table that is private to your module for the purpose rather than the global registry table. In this case, the table ObjManager itself might be a good candidate.

RBerteig