GObject Introspection Tips

GObject comes with a well-thought introspection mechanism that helps building language bindings by adhering to certain conventions. Unfortunately, the documentation is lacking structure and some vital information. Here are some tips, to help you through the initial confusion, if you plan to make your library introspectable:

  • If possible and to avoid a lot of trouble, you should use the Autotools integration as described here. But if you have to use CMake, the following snippet should give you a good start (assuming Foo is your library’s namespacing prefix):

      find_program(INTROSPECTION_SCANNER "g-ir-scanner")
      find_program(INTROSPECTION_COMPILER "g-ir-compiler")
    
      set(GIR_PREFIX "Foo-${FOO_API_VERSION}")
      set(GIR_XML "${GIR_PREFIX}.gir")
      set(GIR_TYPELIB "${GIR_PREFIX}.typelib")
    
      add_custom_command(OUTPUT ${GIR_XML}
          COMMAND ${INTROSPECTION_SCANNER}
                  --namespace=Foo
                  --nsversion=${FOO_API_VERSION}
                  --library=foo
                  --no-libtool
                  --include=GObject-2.0
                  --include=GModule-2.0
                  --output ${GIR_XML}
                  --warn-all
                  ${FOO_SRCS}
          DEPENDS ${FOO_SRCS}
          WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
    
      add_custom_command(OUTPUT ${GIR_TYPELIB}
          COMMAND ${INTROSPECTION_COMPILER}
                  -o ${GIR_TYPELIB}
                  ${GIR_XML}
          DEPENDS ${GIR_XML}
          WORKING_DIRECTORY
          ${CMAKE_CURRENT_BINARY_DIR})
    
      add_custom_target(gir ALL DEPENDS ${GIR_XML} ${GIR_TYPELIB})
      add_dependencies(gir foo)
    

    Unfortunately, the typelib is not found if it is installed into /usr/local/lib/girepository-1.0. You either need to install it into /usr/lib/girepository-1.0 or extend GI_TYPELIB_PATH.

  • If you write interfaces, make sure that the definitions of the “virtual” function pointers in the interface structure match the real functions used to call the actual implementation. In fact, the parameter names should not play a role, but g-ir-scanner is very finicky. Something like this

      struct _FooVehicle {
          void (*accelerate) (FooVehicle *vehicle, guint kmh);
      };
    
      void foo_vehicle_accelerate (FooVehicle *vehicle, guint speed);
    

    will give you a “Foo: Vehicle: Virtual function ‘accelerate’ has no known invoker” although the type signature is the same!

  • Non-GObject-based libraries cannot be inspected. If the user of the bindings does not have to care about interfacing with third-party libraries you can just type their data structures as gpointer but otherwise you must wrap the entire library.

  • You must keep object references in mind and set the transfer annotation appropriately. If you see crashes at the end of a Python session, it’s very likely that the Python interpreter tries to unref an object that has already been destroyed by your library.

  • Be aware of translations into the target language. In C, the canonical constructor would be something like foo_vehicle_new (...) or foo_vehicle_new_with_gasoline (...). In Python, g_object_new is used to instantiate the object thus bypassing any code in the _new functions. Of course, you could still call Foo.Vehicle.new() directly though.

    If you want to make sure that certain parameters are passed at construction time, you should add GObject properties with the G_PARAM_CONSTRUCT or G_PARAM_CONSTRUCT_ONLY flag to your class. To initialize the object after all “constructable” properties are set, you can override the virtual constructed function of the GObject base class.

  • Always put GError arguments at the end of the parameter list or before the final ... argument list. At least with Python, you can then benefit from automatic conversion to real Python exceptions.


Discussion