What is setUserData and why do I need it?
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Re: What is setUserData and why do I need it?
Well if I already have a strong reference elsewhere it sort of defeats the purpose - somebody has to manually modify those refs, right?
Re: What is setUserData and why do I need it?
You will have a strong reference to the body somewhere, and you can access the associated data using the body as the key to the weak table. The weak key means that the element will be automatically removed from the table when the body disappears.
What raidho and I are trying to say is that setUserData/getUserData is redundant, as it could have been implemented in pure Lua (and it can be implemented for any other object as well that doesn't already support it):
Now that I think about it, I no longer think it's thread safe. The context that the function expects won't exist in a different thread, so it can't execute safely.
What raidho and I are trying to say is that setUserData/getUserData is redundant, as it could have been implemented in pure Lua (and it can be implemented for any other object as well that doesn't already support it):
Code: Select all
local objectData = setmetatable({}, {__mode='k'})
local function setUserData(self, data)
objectData[self] = data
end
local function getUserData(self)
return objectData[self]
end
local reg = debug.getregistry()
reg.Body.setUserData = setUserData
reg.Body.getUserData = getUserData
reg.Fixture.setUserData = setUserData
reg.Fixture.getUserData = getUserData
...
-- it can also be used for other objects:
reg.Image.setUserData = setUserData
reg.Image.getUserData = getUserData
reg.Canvas.setUserData = setUserData
reg.Canvas.getUserData = getUserData
...
Re: What is setUserData and why do I need it?
If you have a strong reference somewhere else in the code - the weak ref will never be collected.
I think the user shouldn't have to store a reference in Lua, Box2D objects are destroyed explicitly anyways.
Re: What is setUserData and why do I need it?
That's the point. With this method, the user data will be associated to the object for as long as the object is alive. If you destroy the object, the associated data will be automatically destroyed as well, which is the whole point. That's what lets you implement setUserData/getUserData in Lua, and why I consider them redundant in the physics classes.
Re: What is setUserData and why do I need it?
I don't get it, can you show a code example of how that would work?
Re: What is setUserData and why do I need it?
This example adds setUserData/getUserData capabilities to an Image object and demonstrates them. It also shows the count of elements in the objectData table. When pressing the space bar, the image is deleted and garbage collection is run immediately to show how the weak element is deleted. For some reason, the weak table element isn't removed unless I add two collectgarbage() in a row, but in normal use, that should happen automatically (there just isn't anything going on allocation-wise in this program, so the garbage collector doesn't run unless forced).
Code: Select all
local objectData = setmetatable({}, {__mode='k'})
local function setUserData(self, data)
objectData[self] = data
end
local function getUserData(self)
return objectData[self]
end
local reg = debug.getregistry()
reg.Image.setUserData = setUserData
reg.Image.getUserData = getUserData
local function drawImg(img, x, y)
local data = img:getUserData()
love.graphics.draw(img, x, y, data.angle, data.scale, data.scale,
data.ox, data.oy)
end
local img = love.graphics.newImage('image.jpg')
img:setUserData({angle = math.rad(45), scale = 0.5,
ox = img:getWidth()/2, oy = img:getHeight()/2})
local function numElems(t)
local count = 0
for k, v in next, objectData do
count = count + 1
end
return count
end
function love.draw()
if img then
drawImg(img, love.graphics.getWidth()/2, love.graphics.getHeight()/2)
end
love.graphics.print(tostring(numElems(objectData)))
end
function love.keypressed(k)
if k == "space" then
img = nil
collectgarbage()
collectgarbage()
end
return k == "escape" and love.event.quit()
end
Re: What is setUserData and why do I need it?
The other thread will simply have its own separate userdata storage, on account of residing in a totally separate Lua state. It's perfectly thread-safe.
Re: What is setUserData and why do I need it?
It's a good try, but I don't see how you can ensure that the user doesn't lose his strong reference. I think this would be very difficult to debug especially for box2d callbacks, etc.
Furthermore, if you are going to hack into the debug.registry you might as well overwrite body.create/destroy and don't use weak keys at all.
Furthermore, if you are going to hack into the debug.registry you might as well overwrite body.create/destroy and don't use weak keys at all.
Re: What is setUserData and why do I need it?
I think I get what you mean. I assume that the user will have strong references to the objects they want to manipulate, but that may not be the case. The internal Box2D references don't count as strong Lua references, and the weakref can be destroyed even if the object is alive. Here's a demonstration:
Code: Select all
local objectData = setmetatable({}, {__mode='k'})
local world = love.physics.newWorld(0, 0, true)
print("Before creating body, world contains "..#world:getBodies().." bodies")
local body = love.physics.newBody(world, 0, 0)
objectData[body] = {}
print("Body reference: " .. tostring(body))
print("After creating body, world contains "..#world:getBodies().." bodies")
local function numElems(t)
local count = 0
for k, v in next, objectData do
count = count + 1
end
return count
end
local oldgc = debug.getregistry().Body.__gc
debug.getregistry().Body.__gc = function(...)
print("GC run", ...)
oldgc(...)
end
print("Before GC, obj.data table contains "..numElems(objectData).." elements")
body = nil
collectgarbage()
collectgarbage()
print("After GC, obj.data table contains "..numElems(objectData).." elements")
print("Body reference: " .. tostring(world:getBodies()[1]))
love.event.quit()
--[[ Output:
Before creating body, world contains 0 bodies
Body reference: Body: 0x55fb73722640
After creating body, world contains 1 bodies
Before GC, obj.data table contains 1 elements
GC run Body: 0x55fb73722640
After GC, obj.data table contains 0 elements
Body reference: Body: 0x55fb73722640
GC run Body: 0x55fb73722640
--]]
That was purely for illustrative purposes.
Re: What is setUserData and why do I need it?
It does counts internal references as strong ones, however it doesn't actually keep them. Box2D's internal references are not counted as strong ones because then it would be impossible for physics objects to get garbage collected.
Not sure about calling __gc twice though. Only the source code investigation could tell.
Not sure about calling __gc twice though. Only the source code investigation could tell.
Who is online
Users browsing this forum: Google [Bot] and 3 guests