I’m developing a glib-/gobject-/gio-based application written in C.
Sadly, the documentation is not very helpful when it comes to the usage of public functions, especially in more complicated setups with inheritance.
Originally, I expected that virtual public methods from a derivable class would be callable simply using
obj->method(), but that doesn’t seem to be the case.
#gtk IRC channel suggested that the canonical way to do this is to is to create wrapper functions and setting the parent’s vfunc pointer to the desired… well… wrapper function, really.
To this effect, and also for demonstration purposes, I’ve created a simple libtest project.
(Sorry, I would have linked all mentioned files, but Discourse won’t let new users post more than two links in one post. For reference, though, use the the permalink to the file listing at the point I’m referring to.)
libtest-abstract.h declares an abstract base class and a wrapper function called
lib_test_abstract_foo. The actual implementation in
libtest-abstract.c is simple as well: test if the pointer passed in is of an abstract type and call the virtual public function if it’s not purely virtual. Note that it is purely virtual by default.
libtest-final.h declares a final class, subclassing
LibTestAbstract and an accessor called
lib_test_final_foo. Things get hairy in
libtest-final.c, though. In order to set the vfunc pointer, the parent object struct within the class definition must be accessed using
LIB_TEST_ABSTRACT_CLASS(LibTestAbstractClass*) and then set the foo pointer… well… what? It can’t be set to
lib_test_final_foo because that would take a
LibTestFinal pointer. Since the parent declares the method as taking a parent-object-typed pointer, that would only lead to a compiler warning. I hence created another (static) wrapper function called
lib_test_final_parent_foo which takes a parent-object-typed pointer, checks if it’s actually a final-object-typed pointer though using
LIB_TEST_IS_FINAL, casts the pointer back to a final-object-typed pointer via
LIB_TEST_FINAL and calls the actual implementation using
lib_test_final_real_foo. The “usual” wrapper function
lib_test_final_foo only checks whether the pointer is final-object-typed and calls the actual implementation.
Using that is easy, as shown in
Something about this approach feels very wrong, though - especially what has been done in the
lib_test_final_parent_foo function. Is such casting even legal?
The whole point of virtual methods is to specialize them in derived classes, but gobject makes that terribly difficult (or technically impossible, because you’ll only ever have a parent-object-typed pointer). I understand that this is a limitation of C because it doesn’t have introspection, but it still feels odd.
I’ve looked around the glib source code to find any such usage, but there seems to be… none. Instead, a lot of gio code just calls functions explicitly, without wrapping them or making use of the vfuncs (c.f.,
GSocketAddress and friends). The only examples I was able to find that actually uses the vfuncs probably are IO-related (buffer-based ones) and gapplication/gapplication-glib. The latter calls vfuncs directly via
G_OBJECT_CLASS (..._parent_class)->vfunc (object) instead of using wrappers, though. Interestingly,
object is also parent-object-typed there (i.e.,
I would have expected such a basic feature to see widespread usage?