11

I'm aware of the weak tables functionality in Lua, however I would like to have a weak reference with a single variable.

I've seen this proposal which suggests an API as follows:

-- creation
ref = weakref(obj)
-- dereference
obj = ref()

which would seem ideal. However this doesn't appear to be in the documentation elsewhere; only weak tables.

Is there something analogous to Python's weak reference to object functionality?

Colonel Thirty Two
  • 23,953
  • 8
  • 45
  • 85
MarkNS
  • 3,811
  • 2
  • 43
  • 60
  • 1
    What are you trying to do here ultimately? You can emulate this yourself using a table with at metatable I believe (or with `newproxy` and a metatable) if you really need to. – Etan Reisner Mar 17 '15 at 21:10
  • I'm using middleclass for OO, passing a closure from objA to objB. When objA goes out of scope, the closure is preventing both instances from being GC'd because it contains a reference to objA. Never heard of newproxy before, going to google it up... – MarkNS Mar 17 '15 at 21:16
  • hmmm.. "The undocumented newproxy function was removed in Lua 5.2, since it was made redundant by other features added in that version" from http://lua-users.org/wiki/HiddenFeatures – MarkNS Mar 17 '15 at 21:18
  • 1
    Yeah, you don't need it for this it was just to use a userdata instead of a table for the idea. The idea was a table with a metatable which supported `__call` to get you the `ref()` syntax where either the table itself or the metatable were weak and held the reference. – Etan Reisner Mar 17 '15 at 21:21
  • Interesting.. I've not used metatables too much with Lua. I'll have a read to see if there's something useful there. Thanks. – MarkNS Mar 17 '15 at 21:28
  • You don't need the metatable for the weak-ref idea. Without it you just need to use `ref.real` or `ref.obj` or whatever instead of `ref()`. – Etan Reisner Mar 17 '15 at 22:05
  • "this proposal" is a document from 2004, before you could (I presume) create weak tables directly in Lua (i.e., without using the C API). That's no longer the case nowadays. You got two good answers showing you how to do this. – Niccolo M. Mar 18 '15 at 07:24

2 Answers2

10

When lua does not provide something, there is often a simple way to implement it from the other primitives.

function weakref(data)
    local weak = setmetatable({content=data}, {__mode="v"})
    return function() return weak.content end
end

We create a weak table with just the data in it. Then we return a function that when called, returns the contents of that table.

(Note, weak references may not be broken until garbage collection, and literals are never garbage collected.)

kazagistar
  • 1,537
  • 8
  • 20
  • It's a shame this answer wasn't accepted - it's better than the accepted one, AND came about a minute sooner. – fluffy Jul 05 '17 at 23:38
  • 1
    The other answer has some pretty important things going for it... specifically, it has the potential to create less garbage, since my solution creates an object and a closure over that object for each call, while the accepted solution only creates the table. (Both also spuriously create a new metatable each time as well, but that could be pulled out and reused). – kazagistar Jul 07 '17 at 20:22
  • Ah, good points. Mostly I just needed to learn about `__mode="v"` - in my use case I'm making an image pool and just wanted a weak table where all the values in a table were collectable. – fluffy Jul 08 '17 at 21:44
5

Something like this can do what you want I believe:

local obj = {value = "obj.value"}

local ref = setmetatable({real = obj}, {__mode = "v", __call = function(self) return self.real end})

print(obj.value)
print(ref.real.value)
print(ref().value)

obj = nil
collectgarbage()
collectgarbage()

print(obj)
print(ref.real)
print(ref())

The __call part is optional but gives you the ref() call syntax. Without it you have to use the direct access version.

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148