I don't see why that would cause any problems for the Nim wrappers.
The core problem is, what we can pass objects to the external lib, and later want to get them back or delete a sub-collection of them all.
A very basic gtk3 app has this well known shape:
# nim c button.nim
import gintro/[gtk, glib, gobject, gio]
proc buttonClicked (button: Button) =
button.label = utf8Strreverse(button.label, -1)
proc appActivate (app: Application) =
let window = newApplicationWindow(app)
window.title = "GNOME Button"
window.defaultSize = (250, 50)
let button = newButton("Click Me")
window.add(button)
button.connect("clicked", buttonClicked)
window.showAll
proc main =
let app = newApplication("org.gtk.example")
connect(app, "activate", appActivate)
discard app.run
main()
Here we create a single window and a button, add window and button to the app and wait for events like button-clicked, while gtk main loop is running.
So there is no obvious owner of button widget.
Generally, our app has much more widgets, put in various containers like grids to arrange them.
And, and here starts the trouble: We can get back widgets from GTK, for example we can get back a button from a cell of a grid. We may need the button to modify it, or we may put it into a different container.
Or we may want to delete a row or a column of our grid, which is deleting multiple widgets at one.
It may be not used too often, but this activity is offered by GTK and is generally used.
Currently gintro solves this with GC and proxy objects, that is gtk widgets are wrapped in Nim ref object: For a widget gc_ref is applied when it is passed to a gtk container. GTK may increase a gtk-internal ref counter when the widgets is referenced gtk-intern multiple times, and gtk decreases the internal ref count when widgets are removed. When gtk-intenal ref count is zero then gtk calls Nims gc_unref() with associated finalizer, so the widget can be fully destroyed. I an not sure if this description is really accurate, but that is the basic principle, supported by gtk's toggle_ref concept, which was built to support foreign GC languages. From my observaitons it seems to work well, only drawback is delayed destruction. Delayed destruction can be a serious issue for special cases, for example for drawing operations (cairo) where cairo drawing context and temporary surfaces are allocated in loops called 60 times per sec. (see bottom most example in https://github.com/StefanSalewski/gintro). With this approach sub-classing of widgets works well, as we get back from GTK exactly our Nim object. So when we get for example a subclassed button of Nim type MyButton, then we may get from gtk calls like grid.getFirstChild() (or from a callback function) a Widget as basic type, but we can use Nim's 'of' operator to test for exact subclassed type.
I think the core problem is, that without GC, gc_ref and finalizer, the Nim world and the external lib like GTK do not really know from each other -- it is unclear who owns an object and who may have dangling references. Similar problems may occur with other C/C++ libs like CGAL. For example when we use a geometric data structure from GGAL with vertices and edges, is it obvious who owns the vertices and edges? Maybe similar problems also can occur when we create a pure Nim GUI app using a shape like in GTK. But for a pure Nim app we may base all Widgets on a basic container, which can guarantee a clear ownership.
I think for GTK all that will be difficult with owned refs and without available GC_ref. Maybe, when we restrict ourself to never deleting widgets, then we can collect all used widgets in a Nim container, which owns them. But that is a restriction, even a seriously one when we allocate widgets in loops, for example cairo objects in drawing loops.
On the contrary, the new model shows that even GTK itself longs for this model:
let button = newButton("Click Me")
window.add(button)
button.connect("clicked", buttonClicked)
# can you do that? what would happen then? how can a button be part of two windows at the same time?
otherWindow.add(button)
More seriously though, I still don't understand the problem. Ownership in UIs is pretty simple, the window owns the layout container that owns the widgets which own the strings inside it. People have used manual memory management in Delphi for decades without much trouble, you occasionally had to use a leak detector. The only problem was that it was unchecked at runtime so use-after-free bugs were really hard to diagnose.
use-after-free bugs were really hard to diagnose.
That is the problem. With old runtime and Garbage_Collector we should not have such problems.
With new runtime, and GTK owning the widgets, we may have dandling pointers, which Nim can not detect: Nim code may use an unowned ref to a widget, while GTK is going to delete that widget. But maybe that is not such a great problem in practice.
Funny fact is, that we have Nim GTK4 bindings since a few days, I am going to ship them in the next days. But of course GTK4 is very unstable itself currently. But it is good to have the bindings, was minimal effort due to gobject-introspection.