For my game I have this huge GameState object. One of its fields requires additional cleanup when destroyed.
This field is from the Chipmunk bindings module: https://github.com/oprypin/nim-chipmunk/blob/a17a83ae950f6493752c9aae74d310df6dbf4ddf/src/chipmunk.nim#L120
It is defined as
Space* = ptr object
Space has a reference to all the children it contains (physics things such as Objects, Shapes, Constraints), but it doesn't take ownership of those things when they are added to the space.
Neither is a =destroy proc provided for a space
Over here a normal proc called destroy is created, which can be used to free the Space itself but not any of the things contained within: https://github.com/oprypin/nim-chipmunk/blob/a17a83ae950f6493752c9aae74d310df6dbf4ddf/src/chipmunk.nim#L1788
proc destroy*(space: Space) {.destroy, cdecl, importc: "cpSpaceFree".}
in my code, GameState looks like this:
type GameState* = ref object of RootObj
level*: Level
background*: LCDBitmap
hintsEnabled*: bool
isGameStarted*: bool
# Physics
space*: Space
gravityDirection*: Direction8
[.. and roughly 40 more fields ..]
I tried this:
proc `=destroy`(space: Space) =
print("Destroying Chipmunk space.")
if space != nil:
space.freeSpaceChildren()
space.destroy()
But that doesn't work because Space is not an Object: signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)
So now I'm looking at writing a destructor for GameState, which is an object. But I do not want to manually free all the other fields of GameState, that would be unmaintainable.
Is there a way to create a destroy proc for a ptr object so that I can write a destroy function for Space, or else a way to hook into the generated GameState destructor and do something like this (pseudo code)
override function destroy(state: GameState) {
state.space.freeSpaceChildren()
state.space.destroy()
super.destroy() // call original, generated destroy function for GameState
}
You could try =destroy(x: typeof(newSpace()[])). Essentially you tell the compiler to get the underlying object it makes. Though I'm not sure the destructor would be called since it's a ptr even if it existed. Maybe.
You can also change Space to SpaceBase = object; Space = ptr SpaceBase. Actually do that. Space Base is a nice pun! ;)
You can also use https://nim-lang.org/docs/iterators.html#fieldPairs.i%2CT to iterate over Space's fields and call =destroy on them. Maybe adding a when compiles(=destroy(field)) as some fields don't have destroys. There's also 'fieldPairs' which gives you the name of each field you can use in a 'when' check.
Thanks, I couldn't get your ideas to compile unfortunately.
Example:
proc `=destroy`*(x: GameState) =
echo "Destroying game state"
for name, value in x.fieldPairs:
when compiles(`=destroy`(value)):
echo "Destroying ", name
`=destroy`(value)
two problems here:
The first couple sentences of @elcritch's comment above also applies to ref object types.
i.e. your proc declaration needs to be proc `=destroy`*(x: typeof(GameState()[]))
I also see "unlisted exception" complaints. Not sure where they come from. I got tired of coding try/except blocks to remove them (what can be done in the except block, other than just print or log a message?), and ended up ignoring them.