Introspecting the greeter
Now that we have a GObject class, let's introspect it. This is done by the program g-ir-scanner. This program looks in our C source code and headers for annotations, and also runs the compiled program to get information about installed signals and properties. To make this work, we have to add some code to the program to make it recognize a special command line option.
Add an include to the top of main.c:
#include <girepository.h>
And some code to the main function:
GOptionContext *ctx; GError *error = NULL; ctx = g_option_context_new (NULL); g_option_context_add_group (ctx, g_irepository_get_option_group ()); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_print ("greeter: %s\n", error->message); return 1; }
We also need some changes to the Makefile since we're now also using the gobject-introspection-1.0 package. We also need to link against the gmodule-2.0 library since the GObject Introspection code uses that. (I don't know why pkg-config --libs gobject-introspection-1.0 doesn't include that as a dependency, as it does with glib-2.0 and gobject-2.0.)
To compile this, you need to have the GObject Introspection package installed, including development files. On Debian-based systems such as Ubuntu, sudo apt-get install libgirepository1.0-dev should bring in everything you need.
Recompile with make. The program now has a command line option scanner; type ./greeter --help to verify that it works. You'll see no mention of any option to do introspection, not even with --help-all. This is hidden, since it's not really useful for end users of your program, but it's there:
$ ./greeter --introspect-dump greeter: Missing argument for --introspect-dump
You don't really need to know this to use introspection, but if you're curious about what argument the --introspect-dump option is expecting, see the documentation for g_irepository_dump, to which the argument is sent.
Now, we're ready to run g-ir-scanner. Again, in a real situation we'd use a build tool such as automake to put this together for us, but it can be instructional to try it directly on the command line.
$ g-ir-scanner tut-greeter.[ch] \ --program=./greeter \ `pkg-config --cflags gobject-introspection-1.0` --include=GObject-2.0 \ --namespace=Tut --nsversion=0.1 --output=Tut-0.1.gir
First, we tell the scanner what source files to look in. Then, we tell it that it is a program we're introspecting and not a library, and where to find this program. Next, since the scanner needs to expand the headers in order to correctly parse the code, we need to give it the same pkg-config --cflags output as is used when compiling, so that it can find these headers. We tell the scanner that GObject-2.0 is an introspection dependency to our namespace. Last, we call our namespace Tut with namespace version 0.1 and ask for output to the file Tut-0.1.gir. For a complete reference to g-ir-scanner, see the man page.
Take a good look at the output file. It's an XML file that describes all the classes in our namespace, in our case the TutGreeter, and how they're connected to C code. It's also instructional to take a look at the .gir files of APIs you might be familiar with, such as Gtk+ – you probably have them installed on your system; try locate .gir.
Code that uses your introspected classes don't read the XML directly though, that would be inefficient. Instead, we convert it to a binary format called typelib. This is done with g-ir-compiler:
$ g-ir-compiler Tut-0.1.gir --output=Tut-0.1.typelib
See the g-ir-compiler man page for full reference.
Exercises
Using the programming language and an XML parsing library of your choice, such as Python with lxml, write a simple script to extract the names of all classes and their methods from a .gir file.
Try getting introspection right for the tut_greeter_greet_many and tut_greeter_get_greet_log methods introduced in the exercises on the previous page.