Hiya! I'm writing out some code and noticed something a little funky. I wanted to procedurally generate entries in a Slab tree. What I'm doing is writing out a level editor for an ECS-based game; so, I want a big ol' tree showing the hierarchy of entities and what components they have and what properties those components have and so on. But, when I have a few hundred entities and they're spawning in and despawning all the time, I don't have a fixed set of IDs to use for Slab objects. So, I generate IDs from the indices of these entities in the world object. This means every entity gets an ID in [1, #world.entities]
, and then I use "entity@" .. i
to generate a Slab id and also a label for the tree node. Then, I do a similar thing for every component in that entity, to create child tree nodes; and then leaf nodes as children of each component node.
The issue with this is that when an entity is destroyed, I no longer need all the data associated with it which is kept around by Slab. If I create thousands and thousands of entities and then destroy them, I end up with thousands and thousands of entries in Slab's Instances
tables which I don't need, and Slab's Instance
tables are live for the lifetime of my program, not for the lifetime of my editor scene. So, really, what I get is a memory leak.
I have a few suggestions on ways this could be fixed. They range from simple to a possibly invasive redesign of how Slab handles Ids.
The first and simplest would be a "DeleteXxxx" function for a given Slab widget/object (DeleteWindow
, DeleteInput
, DeleteButton
, etc.) which just nils out the given entry. A quick browse through Slab's source suggests this should work fine... but I've been using and looking at Slab's source for a little over a day, so grain of salt, etc. Among the obvious consequence of deleting a Slab instance, there is also the issue that this would cause the instance to not be saved in Slab.ini
, but as long as that's documented I don't see a problem.
The second way would be to have an option for Slab to go through and track widgets which haven't been updated/drawn recently, and remove them from their corresponding Instances table. This could get a bit expensive... And would also interfere with Slab's state-saving feature. Actually, everything about this feature interferes with Slab's state-saving, so if implemented it needs to be optional no matter what, I suppose.
The third and most interesting/convenient/user-friendly way, and the one that entertains and interests me the most, would be to allow non-string IDs, and make all Instances
tables have a metatable with __mode = "k"
. This could be done with local Instances = setmetatable({}, { __mode = "k" })
, as the __mode
metatable entry must be set before the metatable is assigned. What this does is make the Instances
table have "weak keys". This has the major consequence that if an ID is no longer "strongly" referenced anywhere in a Lua program, then its entry in the Instances
table will be removed from the table. I like this solution for a handful of reasons:
- It shouldn't require much modification; I know string keys are constructed by concatenation in a few places, but for the most part I think it should be possible to lift the IDs-must-be-strings restriction without a lot of work.
- Whether or not something has its state saved in
Slab.ini
can be determined by whether or not its key is a string. Instances made with non-string IDs can just not be saved, which make Slab.ini
smaller for programs like mine.
- In my particular use case, I can do something like use my entity's table itself as a Slab ID, and when the entity no longer exists, the corresponding Slab object will also be garbage collected, and without Slab having to do anything itself as the Lua/LuaJIT GC handles it all.
- If all IDs are strings, setting
__mode = "k"
will do nothing at all because strings will never be garbage collected (see Programming in Lua Ch. 17).
I'm very interested in the third option, so even if it doesn't seem like a good solution for Slab, I'm going to try adding it to my own little branch. :D
If anyone has time to give some feedback on these ideas, I would appreciate it a lot. This is my first time using an immediate mode GUI library and Slab is making my life a lot easier. :)