G_object_unref and ctypes

Recentlly I’ve tried to write a small Hello World program using ctypes and gtk4. My problem is g_object_unref which is called when the application has already run fails with SIGSEGV (Address boundary error)

I run the program (on Fedora 36) with ocaml hello.ml and here is the program:

#use "topfind";;

#require "ctypes";;
#require "ctypes.foreign";;

open Ctypes;;
open Foreign;;

let gtk4 = Dl.dlopen ~flags:Dl.[RTLD_NOW] ~filename:"libgtk-4.so";;
let gtk4 = foreign ~from:gtk4;;

let gio = Dl.dlopen ~flags:Dl.[RTLD_NOW] ~filename:"libgio-2.0.so";;
let gio = foreign ~from:gio;;

let gobject = Dl.dlopen ~flags:Dl.[RTLD_NOW] ~filename:"libgobject-2.0.so";;
let gobject = foreign ~from:gobject;;

type window = unit ptr;;
type application = unit ptr;;
type widget = unit ptr;;

let window : window typ = ptr void;;
let application : application typ = ptr void;;
let widget : widget typ = ptr void;;

type gpointer = unit ptr;;
let gpointer : gpointer typ = ptr void;;

let callback_t = application @-> gpointer @-> returning void;;

let application_new =
  gtk4 "gtk_application_new" (string @-> int @-> returning application);; 

let application_window_new =
  gtk4 "gtk_application_window_new" (application @-> returning window);;

let window_set_title =
  gtk4 "gtk_window_set_title" (window @-> string @-> returning void);;
let window_set_default_size =
  gtk4 "gtk_window_set_default_size" (window @-> int @-> int @-> returning void);;

let widget_show =
  gtk4 "gtk_widget_show" (widget @-> returning void);;

let application_run =
  gio "g_application_run" (application @-> int @-> gpointer @-> returning int);;
let application_run app = application_run app 0 null;;

let object_unref =
  gobject "g_object_unref" (ptr void @-> returning void);;

let signal_connect =
  gobject "g_signal_connect_data" (application @-> string @-> funptr callback_t @-> gpointer @-> returning void);;

let activate : application -> gpointer -> unit =
  fun app data -> 
  let win = application_window_new app in
    window_set_title win "Hello Ctypes";
    window_set_default_size win 200 200;
    widget_show win

let main () =
  let app = application_new "org.example.hello" 0 in
    signal_connect app "activate" activate null;
    let status = application_run app in
      object_unref app;
      print_endline @@ string_of_int status

main ();;

I can guess why this may happen, but no idea how to fix it.

You are lying to Ctypes about the signature of function g_signal_connect_data and thus passing garbage to it. You should have something like:

let signal_connect app s cb p =
  gobject "g_signal_connect_data" (application @-> string @-> funptr callback_t @-> gpointer @-> gpointer @-> int @-> returning void) app s cb p null 0

Thank you that’s it. g_signal_connect in the C example is a “function macro”, I should have paid more attention to its expansion. Not just the function name