Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The ARC cast issue #133

Closed
StefanSalewski opened this issue May 28, 2021 · 4 comments
Closed

The ARC cast issue #133

StefanSalewski opened this issue May 28, 2021 · 4 comments

Comments

@StefanSalewski
Copy link
Owner

OK, I have done a few tests, and now we have an initial idea about the issue:

import gintro/[gtk4, gobject, gio]

proc setup_cb(factory: SignalListItemFactory; listitem: ListItem) =
  let lb = newLabel()
  listitem.setChild(lb)

proc bind_cb(self: SignalListItemFactory; listitem: ListItem) =
  let lb: Label = Label(listItem.getChild)
  let strobj: StringObject = cast[StringObject](listItem.getItem)
  #let strobj: StringObject = stringObject(listItem.getItem)
  let text = strobj.getString
  lb.setText(text)

proc unbind_cb(self: SignalListItemFactory; listitem: ListItem) =
  # There's nothing to do here.
  # If you does something like setting a signal in bind_cb,
  # then disconnecting the signal is necessary in unbind_cb.
  discard

proc teardown_cb(factory: SignalListItemFactory; listitem: ListItem) =
  listitem.setChild (nil)
  # When the child of listitem is set to NULL, the reference to GtkLabel will be released and lb will be destroyed.
  # Therefore, g_object_unref () for the GtkLabel object doesn't need in the user code.

# ----- activate, open, startup handlers -----
proc app_activate(app: Application) =
  echo "activate"
  let win = newApplicationWindow(app)
  win.setDefaultSize(600, 400)
  let scr = newScrolledWindow()
  win.setChild (scr)
  let arrax = ["one", "two", "three", "four"]
  # let sl: gio.ListModel = cast[ListModel](newStringList(arrax)) # GtkStringList — A list model for strings
  let hhh = newStringList(arrax)
  #GC_ref(hhh)
  #let sl: gio.ListModel = listModel(hhh)
  let sl: gio.ListModel = cast[ListModel](hhh)
  #GC_ref(sl)
  let ns: NoSElection = newNoSelection(sl) # A selection model that does not allow selecting anything
  let factory: SignalListItemFactory = newSignalListItemFactory()
  factory.connect("setup", setup_cb)
  factory.connect("bind", bind_cb)
  factory.connect("unbind", unbind_cb)
  factory.connect("teardown", teardown_cb)
  let lv = newListView(ns, factory)
  scr.setChild (lv)
  win.show

proc app_startup(app: Application) =
  echo "startup"

proc main =
  let app = newApplication("org.gtk.example") # "com.github.ToshioCP.list1"
  app.connect("startup", app_startup)
  app.connect("activate", app_activate)
  let status = app.run
  quit(status)

main()

The program code is from one of the other examples of that gavr123456789, it may be incorrect, but when we compile with --expandArc:app_activate

finally:
  `=destroy`(lv)
  `=destroy_1`(factory)
  `=destroy_2`(ns)
  `=destroy_3`(sl)
  `=destroy_4`(hhh)
  `=destroy_5`(scr)
  `=destroy_6`(win)
-- end of expandArc ------------------------

calling destroy for hhh and sl: gio.ListModel = castListModel is not really intended.

@StefanSalewski
Copy link
Owner Author

@gavr123456789

I think you have indeed discovered a bug in the gintro bindings. From

https://discourse.gnome.org/t/gtk-no-selection-new/6576

and

nim-lang/Nim#18130

it looks that the cast suppresses the wrong destroy. Discovering that took me some hours, but I think the fix will not be that hard. For GObject proc parameters with direction in and transfer full we have to avoid that the Nim memory management destroys the GObject when the Nim proxy object is destroyed.

StefanSalewski added a commit that referenced this issue Jun 1, 2021
@StefanSalewski
Copy link
Owner Author

OK, the fix is available and can be installed with

nimble uninstall gintro
nimble install gintro@#head

The fix is indeed tiny:

diff ~/gintrotest/tests/gen.nim gen.nim 
3c3
< # v 0.9.0 2021-JUN-01
---
> # v 0.8.9 2021-APR-11
1514,1515c1514
<       if true: # https://discourse.gnome.org/t/gtk-no-selection-new/6576
<       #if ((gFunctionInfoGetFlags(mInfo).int and GIFunctionInfoFlags.IS_CONSTRUCTOR.int) == 0):
---
>       if ((gFunctionInfoGetFlags(mInfo).int and GIFunctionInfoFlags.IS_CONSTRUCTOR.int) == 0):
3245,3248d3243
< proc stringObject*(self: gobject.Object): StringObject =
<   assert(toBool(g_type_check_instance_is_a(cast[ptr TypeInstance00](self.impl), gtk_string_object_get_type())))
<   cast[StringObject](self)
< 
4130c4125
< # 4130 lines
---
> # 4125 lines

Now this code should work:

import gintro/[gtk4, gobject, gio]

proc setup_cb(factory: SignalListItemFactory; listitem: ListItem) =
  let lb = newLabel()
  listitem.setChild(lb)

proc bind_cb(self: SignalListItemFactory; listitem: ListItem) =
  let lb: Label = Label(listItem.getChild)
  let strobj: StringObject = stringObject(listItem.getItem)
  lb.setText(strobj.getString)

proc unbind_cb(self: SignalListItemFactory; listitem: ListItem) =
  # There's nothing to do here.
  # If you does something like setting a signal in bind_cb,
  # then disconnecting the signal is necessary in unbind_cb.
  discard

proc teardown_cb(factory: SignalListItemFactory; listitem: ListItem) =
  listitem.setChild (nil)
  # When the child of listitem is set to NULL, the reference to GtkLabel will be released and lb will be destroyed.
  # Therefore, g_object_unref () for the GtkLabel object doesn't need in the user code.

# ----- activate, open, startup handlers -----
proc app_activate(app: Application) =
  echo "activate"
  let win = newApplicationWindow(app)
  win.setDefaultSize(600, 400)
  let scr = newScrolledWindow()
  win.setChild (scr)
  let sl: gio.ListModel = listModel(newStringList("one", "two", "three", "four"))
  let ns: NoSElection = newNoSelection(sl) # A selection model that does not allow selecting anything
  let factory: SignalListItemFactory = newSignalListItemFactory()
  factory.connect("setup", setup_cb)
  factory.connect("bind", bind_cb)
  factory.connect("unbind", unbind_cb)
  factory.connect("teardown", teardown_cb)
  let lv = newListView(ns, factory)
  scr.setChild (lv)
  win.show

proc app_startup(app: Application) =
  echo "startup"

proc main =
  let app = newApplication("org.gtk.example") # "com.github.ToshioCP.list1"
  app.connect("startup", app_startup)
  app.connect("activate", app_activate)
  let status = app.run
  quit(status)

main()

Instead of the cast we can now use two converter procs listModel() and stringObject(). The first was already available before but generated a crash, it was generated automatically. The stringObject() converter proc had to be added manually, as we see no clear pattern for its creation. We may need more of such converters.

Not that we have to call these converter procs manually, as the gintro bindings avoids to use real converters which are called fully automatic. Using converter procs is only necessary in rare cases, and it is easy, but unfortunately we have to document their existence carefully so that people can find them. But of course a plain cast would work also.

@gavr123456789
Copy link

Just tested, all works perfectly, thank you.

@StefanSalewski
Copy link
Owner Author

OK, we can close this issue then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants