Now that finalizers work fine with --gc:arc there are two questions left:
https://nim-lang.org/docs/system.html#new%2Cref.T%2Cproc%28ref.T%29 >Note: The finalizer refers to the type T, not to the object! This means that for each object of type T the finalizer will be called!
If that a fundamental restriction, or is that done only to not waste 8 bytes for a pointer to the actual finalizer in each instance? The point is, that there may exists circumstances where one really wants a finalizer bound to each instance. One example are objects returned from gtk libs. In most cases function results are of type "transfer full", indicating that user takes ownership and has to free the object. For this case we employ a finalizer. But there are cases where gtk still owns the object, and we would not need a finalizer. In really rare cases for the same class these two cases are mixed. That is a problem in Nim, because for the same proxy object we have to decide if we employ a finalizer or not. Of course we can solve this easily when we give the proxy object a boolean field indicating if the finalizer shall really free the gtk object or just do nothing.
The other question is about the fact that finalizer has to be defined in the same module as the object is defined. Finalizer allows bindings the same function multiple times now, which is very useful. I assume that the restriction to one module is because the compiler can not ensure that always the same function is used as finalizer for instances of an object if the finalizer proc lives in a different module? (This is indeed no serious restriction, I can simple copy the gobject finalizer proc into each module that uses subclasses of gobject.)
If that a fundamental restriction, or is that done only to not waste 8 bytes for a pointer to the actual finalizer in each instance?
It used to be the case that we really wanted to save these 8 bytes but now if we map them to destructors, it's quite a fundamental restriction.
Of course we can solve this easily when we give the proxy object a boolean field indicating if the finalizer shall really free the gtk object or just do nothing.
Seems to be a good solution.
I assume that the restriction to one module is because the compiler can not ensure that always the same function is used as finalizer for instances of an object if the finalizer proc lives in a different module?
At some point we need to know how to generate code for these things and delaying code generation "until the full program has been analysed" is a bad, non-modular design that would hurt us hard very soon once incremental recompilations materialize.
This is indeed no serious restriction, I can simple copy the gobject finalizer proc into each module that uses subclasses of gobject.
Good, I'm glad my research prototype is becoming production ready. :-)
The fact that the finalizer is specified on the new proc still means that is called only for that particular instance?
"still means"? No, it never was that way, it was attached to the type, albeit in a weird way.
I would prefer something associated directly to the type.
Yes, that's called a destructor. Now a thing in Nim.
So destructor or finalizers are the same thing?
Yes.
So what's the point of keeping both?
Compatibility.
Should I move from finalizers to destructors?
No, ideally you keep your code as it is so that it continues to work with older Nim versions.
i would deprecate finalizers
I may agree, but i think automatic calling of destructors for references does only work with devel compiler and --gc:arc? At least this test was only working with arc some weeks ago:
https://forum.nim-lang.org/t/5786
So code will be incompatible for old/new Nim.
I did just this test with latest devel compiler:
type
Object* = ref object of RootRef
impl*: pointer
type
InitiallyUnowned* = ref object of Object
type
Widget* = ref object of InitiallyUnowned
proc finalizeGObject*[T](o: ref T) =
echo "finalizeGObject"
proc finalizeDiscard*[T](o: ref T) =
echo "discard"
proc main =
var w: Widget
#new(w, finalizeDiscard)
new(w)
echo "OK"
new(w, finalizeGObject)
echo "OK"
main()
$ nim c --gc:arc -r t.nim
Hint: gcc -o /tmp/hhh/t /tmp/stefan/.cache/nim/t_d/stdlib_allocators.nim.c.o /tmp/stefan/.cache/nim/t_d/stdlib_io.nim.c.o /tmp/stefan/.cache/nim/t_d/stdlib_system.nim.c.o /tmp/stefan/.cache/nim/t_d/@mt.nim.c.o -ldl [Link]
Hint: 20053 LOC; 0.147 sec; 24.605MiB peakmem; Debug build; proj: /tmp/hhh/t.nim; out: /tmp/hhh/t [SuccessX]
Hint: /tmp/hhh/t [Exec]
OK
finalizeGObject
OK
finalizeGObject
So interleaved calls of new() with and without finalizer compiles now, which may be not really intended by user. To ensure that a finalizer is not called unintentionally one may pass a dummy one for each call as finalizeDiscard() above, that we will get a compiler error abound rebinding. I think I will do that for gintro, without danger of calling active finalizer unintentionally is too high. Later I may indeed use destructors, that may be even easier for code generation, as for finalizers we have to always use full qualification with module prefix matching to object, like new(w, gtk.finalizeGObject) when w is a gtk.Widget.
The good news is, that gintro compiles already with ARC and that at least some examples work. And testing is really deterministic now, no need for GC_fullcollect() and hoping that GC may be not sleeping still :-)
https://github.com/StefanSalewski/gintro#extending-or-sub-classing-widgets
CountButton is a subclass of gtk.Button in user module count_button.nim, so initButton() will not compile as it tries to use a finalizer of gtk module. And without finalizer the gtk widget is not destroyed when the Nim object is collected/destroyed.
I have discovered this problem today, and have still no solution. (Well, composition instead of inheritance should work. But that is a serious restriction.)