0
votes

Im pushing a c++ object pointer to userdata from few different places in my c++ code. I would like lua to manage the lifetime of the c++ object (userdata). My problem is that now I have multiple instances of userdata pointing to the same c++ object in the lua environment. So GC will be called when each instance.

I was thinking that one solution would be to create some weak cache table in the lua registry (LUA_REGISTRYINDEX) to map object pointer to the actual userdata. Then when I push userdata to the environment I check this cache to see if the userdata has already been created and push that instance (otherwise create userdata and add to the cache). That way only one instance of the userdata exists in the environment.

Is this the best solution or am I missing something?

1
This is exactly my goto solution for this kind of problem. It's basically the same approach as Lua's string interning. It simplifies equality checking and the lifetime handling is covered as well.siffiejoe

1 Answers

0
votes

The correct answer is to stop doing this:

I have multiple instances of userdata pointing to the same c++ object in the lua environment

When you give an object to Lua, then Lua owns that object. If a pointer to that object finds its way back into C++, then those C++ APIs should not be able to grant ownership of that object to anywhere else. Including back to Lua again. So there shouldn't be a bunch of functions that can return points to the same object to Lua.

And if you do have a bunch of such functions, you need to re-evaluate whether Lua should have ownership of these objects, or whether it should just be able to use them. You'd be surprised how rarely you genuinely need to give ownership of objects to Lua.

If you absolutely cannot avoid transferring ownership, then this means that your ownership semantics are not strict. That is, there isn't a single system which owns an object. You share ownership of an object with several places.

In C++, that's spelled shared_ptr. Therefore, your userdata should store a shared_ptr<T> to the object being managed. The GC should destroy the shared_ptr, which will only destroy the managed T if all other instances of shared_ptrs which share ownership with it have been destroyed.