I was just working a bit on gintro for latest GTK4 and got a really confusing error message.
Luckely I was just able to generate a minimal example, but I have still no real idea what the problem may be. I guess there must be something special in this code, because I have used similar constructs before and it was working fine...
type
Event* = ref object of RootRef
impl*: pointer
ignoreFinalizer*: bool
template fnew*(a: untyped; finalizer: untyped) =
when defined(gcDestructors):
new(a)
else:
new(a, finalizer)
proc `ref`*(self: Event): Event =
fnew(result, generic_gdk_event_unref)
echo "result.impl = gdk_event_ref(cast[ptr Event00](self.impl))"
proc generic_gdk_event_unref*[T](self: ref T) =
if not self.ignoreFinalizer:
echo "gdk_event_unref(self.impl)"
when defined(gcDestructors):
proc `=destroy`*(self: var typeof(Event()[])) =
if not self.ignoreFinalizer and self.impl != nil:
echo "gdk_event_unref(self.impl)"
self.impl = nil
proc main =
var x: Event
fnew(x, generic_gdk_event_unref)
main()
$ nim c --gc:arc t2.nim
/tmp/hhh/t2.nim(21, 3) Error: cannot bind another '=destroy' to: Event:ObjectType; previous declaration was constructed here implicitly: /tmp/hhh/t2.nim(13, 3)
$ nim -v
Nim Compiler Version 1.3.3 [Linux: amd64]
Compiled at 2020-05-11
Copyright (c) 2006-2020 by Andreas Rumpf
git hash: 7c24250a575b4d441ba6d7301714cbb246425fb6
active boot switches: -d:release
I know why it doesn't compile but my reply would be identical to the compiler's error message. You need to define your destructor earlier, like so:
when defined(gcDestructors):
proc `=destroy`*(self: var typeof(Event()[])) =
if not self.ignoreFinalizer and self.impl != nil:
echo "gdk_event_unref(self.impl)"
self.impl = nil
template fnew*(a: untyped; finalizer: untyped) =
when defined(gcDestructors):
new(a)
else:
new(a, finalizer)
proc `ref`*(self: Event): Event =
fnew(result, generic_gdk_event_unref)
echo "result.impl = gdk_event_ref(cast[ptr Event00](self.impl))"
proc generic_gdk_event_unref*[T](self: ref T) =
if not self.ignoreFinalizer:
echo "gdk_event_unref(self.impl)"
need to define your destructor earlier
Yes, my initial guess was indeed that, as it was done in that way in the earlier gintro code.
But I became unsure if it would work really fine also that way.
And it remains strange, as my fnew template should fully hide the use of the finalizer when we compile with --gc:arc. So I assume that both branches of the when construct are processed by the compiler and that confuses the compiler? In the same way as discarded code is always syntax checked?
Unfortunately ordering the code is not that easy, as it is autogenerated from the gobject-introspection database, and for latest gtk 3.98.3 which will become 4.0 there are some serious changes. I can not just put the =destroy just after the type declaration, as =destroy calls low level procs like gdk_event_unref(self.impl) above. So carefully sorting will be necessary.
Note that forward declarations like below do not compile:
proc gdk_event_unref(self: ptr Event00)
proc gdk_event_unref(self: ptr Event00) {.importc, libprag.}
Error: pragmas are only allowed in the header of a proc; redefinition of 'gdk_event_unref'
What should work is declaring a special unref function before declaring the =destroy and using that one in the =destroy like
proc destroy_gdk_event_unref(self: ptr Event00) {.importc: gdk_event_unref, libprag.}