Basic documentation on gobject introspection API

I was hoping to get some idea of what I need to do to use dynamic ffi bindings to call GTK3 functions from an interpreted language written in C.

I tried the example at libgirepository — GObject Introspection with the libgirepository-2.0-dev Debian package installed but it seems this example is for an earlier version and the function names have changed grom g_ixxx to gi_xxx but there is no function gi_repository_get_default. There is a link libgirepository API Reference: libgirepository API Reference on that page to the API documentation, but the ffi section is marked TODO.

Can somebody point me to a version of this example that I can compile?

Thanks in anticipation
Ian Grant

This discussion was helpful: Steps to get started creating a new language binding? I guess I shouldn’t be trying to use libgirepository-2.0-dev? I assumed that because it had got into the Debian package repository it must be supported in some sense of the word. The overview Overview — GObject Introspection gives no information regarding use of version 1.0 vs version 2.0.

The documentation you’re looking at is the “old” girepository API, which is very undocumented.

The new version of girepository is part of GLib, now: GIRepository – 3.0

There are no “examples” on how to write a language binding because every binding is its own thing; any example would be virtually undistinguishable from an actual project. Additionally, the girepository API was grown, rather than designed, from the needs of three specific language bindings:

  • pygobject
  • gjs
  • perl-glib

All of these bindings had a native GLib/GObject type system implementation, as well as the native trampolines needed to call symbols (as well as caching callables).

It seems you have inadvertently tried to use girepository-3.0 instead of 2.0. The API was changed when the library was copied into GLib (hence the different, parallel installable library and namespace); there is a porting guide: GIRepository – 3.0: Migrating from girepository-1.0 to girepository-2.0

The singleton is coming back in the next version of girepository-3.0, incidentally; but for language bindings without integration with other libraries, like libpeas, you can create your own singleton GIRepository instance.

Thank you. Maybe someone could put in some pointers to the old pages to redirect people to the current version? But I am still very confused, because I have a recent Ubuntu installation and it offers only libgirepository-1.0 and libgirepository-2.0-dev packages.

It would still be a help to see a minimal example of one call from a well-known, well-understood language like C so that people contemplating writing a binding for an interpreter written in C to some library can get an idea of what they will need to do. I am trying to build the example the debian package installed on my system in /usr/share/doc/libgirepository-1.0-dev/examples/girepository which is almost identical to the one on the old web page: libgirepository — GObject Introspection it compiles OK but core dumps

./glib-print
**
domain:ERROR:glib-print.c:42:main: hello world
Bail out! domain:ERROR:glib-print.c:42:main: hello world
Aborted (core dumped)

The backtrace shows it was in libglib-2.0.so.0 which I have no deug symbols for on this system:

> gdb ./glib-print /var/lib/apport/coredump/core._home_ian_girepository_builddir_glib-print.1000.7f9f6708-5710-44be-9c94-04be22158028.17045.2018565 
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
...
Reading symbols from ./glib-print...
[Thread debugging using libthread_db enabled]                                                                                            
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `./glib-print'.
Program terminated with signal SIGABRT, Aborted.
Download failed: Invalid argument.  Continuing without source file ./nptl/./nptl/pthread_kill.c.
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimised out>) at ./nptl/pthread_kill.c:44

warning: 44	./nptl/pthread_kill.c: No such file or directory
(gdb) bt
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimised out>) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=<optimised out>) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=<optimised out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x000078cba944527e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x000078cba94288ff in __GI_abort () at ./stdlib/abort.c:79
#5  0x000078cba973af5b in ??? () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#6  0x000078cba96aab16 in ffi_call_unix64 () at ../src/x86/unix64.S:104
#7  0x000078cba96a73ef in ffi_call_int
    (cif=cif@entry=0x7ffe160c8320, fn=<optimised out>, rvalue=<optimised out>, avalue=<optimised out>, closure=closure@entry=0x0)
    at ../src/x86/ffi64.c:673
#8  0x000078cba96aa0be in ffi_call (cif=0x7ffe160c8320, fn=<optimised out>, rvalue=<optimised out>, avalue=<optimised out>)
    at ../src/x86/ffi64.c:710
#9  0x000078cba987659b in g_callable_info_invoke () at /lib/x86_64-linux-gnu/libgirepository-1.0.so.1
#10 0x000078cba98771fe in g_function_info_invoke () at /lib/x86_64-linux-gnu/libgirepository-1.0.so.1
#11 0x000060bf07845366 in main () at ../glib-print.c:34
(gdb)

I checked, and the compiler line I used has ‘-O0’ so I don’t really understand those

ffi_call_xxx ... fn=<optimised out>, rvalue=<optimised out>

Maybe my problems are because of the way Ubuntu/debian install libraries and distribute debug symbols etc. Maybe I should be building everything from source? But then which version should I use 1.0, 2.0 or 3.0? And I’m not sure which version of libffi I should be using either. I have 3.4.6 which is called libffi8, and it is from the future as far as libffi is concerned (there it says " libffi-3.4.5 was released on February 15, 2024" but if I look at GitHub - libffi/libffi: A portable foreign-function interface library. I see “libffi-3.5.0-rc0 was released on June 2, 2025” but in the HISTORY file there on the same page I see “3.4.8 Apr-9-2025” is the latest released version)

dpkg-query --list | grep libffi
ii  libffi-dev:amd64                                 3.4.6-1build1                             amd64        Foreign Function Interface library (development files)
ii  libffi8:amd64                                    3.4.6-1build1                             amd64        Foreign Function Interface library runtime

it seems that’s what it supposed to do, it calls the function assertion_message, which is a function for crashing with an error

g_irepository_find_by_name (repository, "GLib", "assertion_message");

this is a weird example but it seems it works

Yes, you’re right! It seems the theme here is documentation. If you look at the page GLib.assertion_message you get an idea of the problem: ‘No description available’ is repeated six times, once for the function itself, and once for each argument! See glib/glib/gtestutils.c at a6445ee3c40043b202ad830a50bb7294c2b013fd · GNOME/glib · GitHub

So I have libgirepository-1.0 working, but no real idea of whether I should go ahead with using this, rather than 2.0 or 3.0. Other projects such as the guile bindings for gtk seem to have been able to use 1.0, so I guess it’s OK, and if there will not likely be any documentation available for the 2.0 or or 3.0 in the near future then that is the way to go.

Thanks. But I can’t find anything that tells me what C function to call to create a GIRepository instance. I imagine it’s something in https://docs.gtk.org/gobject/ but what?

Answering my own question: I guess it’s just calling the constructor, doh! https://docs.gtk.org/girepository/ctor.Repository.new.html so just change the call to gi_repository_new

#include <girepository/girepository.h>

int
main (void)
{
  GIRepository *repository;
  GError *error = NULL;
  GIBaseInfo *base_info;
  GIArgument in_args[5];
  GIArgument retval;

  repository = gi_repository_new (); // caller's responsibility to free
  gi_repository_require (repository, "GLib", "2.0", 0, &error);
  if (error)
    {
      g_error ("ERROR: %s\n", error->message);
      return 1;
    }

  base_info = gi_repository_find_by_name (repository, "GLib", "assertion_message");
  if (!base_info)
    {
      g_error ("ERROR: %s\n", "Could not find GLib.assertion_message");
      return 1;
    }

  in_args[0].v_pointer = (gpointer)"domain";
  in_args[1].v_pointer = (gpointer)"glib-print.c";
  in_args[2].v_int = 42;
  in_args[3].v_pointer = (gpointer)"main";
  in_args[4].v_pointer = (gpointer)"hello world";

  if (!gi_function_info_invoke ((GIFunctionInfo *) base_info,
                               (const GIArgument *) &in_args,
                               5,
                               NULL,
                               0,
                               &retval,
                               &error))
    {
      g_error ("ERROR: %s\n", error->message);
      return 1;
    }

  gi_base_info_unref (base_info);

  free (repository);

  return 0;
}

The meson.build file is slightly different too:

project('girepository-sample', 'c')

girepo_dep = dependency('girepository-2.0')

executable('glib-print', 'glib-print.c',
  dependencies: girepo_dep,
)

This is with the Ubuntu/Debian libgirepository-2.0-dev package installed:

sudo apt install libgirepository-2.0-dev