GObject OOP syntax
I'm looking for a GObject cheat sheet, how general OOP concepts correspond to GObjects. Consider for example:
AnyGObject *o; o = anygobject_new();
Now what are the conventions for ...
- method call
- calling a method declared by the base class
- calling a method declared by an interface that the class implements
- class method call
- calling a method of the class declared by the base class
- virtual method call
- casting to base class
- derivative casting
- casting into an interface implemented by a class
- checking if an object is of a specific type
- ...
Clarification: The GObject Reference Manual and the GObject HowTo explain in detail how to create a new class (class struct, object struct, private struct, various macros, conventions). Taken together, these tools enable OOP. However, there seems to be no tutorial on how to use them consistently.
source to share
This answer assumes that you are working in C. Other (usually object-oriented) languages have special bindings designed to make GObject feel more natural.
If you've worked with GTK +, you've already made the most of this list.
GObject methods are not themselves members (there is a kind of vtable, but it is only used to assign implementations of virtual methods in a derived class when the class is first created). Instead, all methods in GObject are just simple functions, usually (?), Prefix the method name prefix, and a pointer this
as the first argument.
For example the C ++ method
namespace Lib { // short for Library; to demonstrate GObject idiomatic naming conventions
class Foo {
public:
void Bar(int z);
};
}
will be a simple function in the global namespace, declared as
void lib_foo_bar(LibFoo *foo, int z);
and you will call it directly like any other C function.
The class in GObject is derived by having the complete data structure of the parent class as the first element of the data structure of the derived class. For various reasons related to rarely discussed articles in the C standard (and possibly the System V ABI and implementations of gcc, clang, and even Microsoft C compilers), this means that a pointer to a derived class object is equivalent to a pointer to a parent class!
So, if LibBaz
comes from LibFoo
, all you have to say is
LibFoo *foobaz = (LibFoo *) baz;
and the same applies in reverse order:
LibBaz *bazfoo = (LibBaz *) foo;
(This last approach is what GTK + uses for GtkWidget
; I don't know if the other GObject libraries do the same.)
The Idiomatic GObject declarations include a bunch of macros that make type conversions more complex and at the same time add runtime type safety checks. Our class LibFoo
would have the following macros:
#define LIB_TYPE_FOO (lib_foo_get_type())
#define LIB_FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), LIB_TYPE_FOO, LibFoo))
With this, we'll say instead
LibFoo *foobaz = LIB_FOO(baz);
LibBaz *bazfoo = LIB_BAZ(foo);
and if either baz
or is foo
not the correct type to convert, a warning will be logged to standard error, which you can break down and investigate with a debugger.
(The function lib_foo_get_type()
(and macro macros LIB_TYPE_FOO
) is important: it returns a numeric identifier that maps to the type LibFoo
for future reference. If it LibFoo
doesn't have such a mapping, it will create a mapping, register the type, and create virtual method mappings.)
A similar macro allows type checking:
#define LIB_IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIB_TYPE_FOO))
which is a simple expression that can be used in a statement if
.
What about calling methods of the parent class? Well, if we put it all together, we have the answer:
lib_foo_parent_method(LIB_FOO(aLibBazInstance), params);
The same goes for virtual methods. Virtual methods are implemented using GObject's approximation to vtables and are transparent to the end programmer. All you do is
lib_foo_virtual_method(LIB_FOO(whatever), params);
(If you are indeed creating the derived class itself, the ways of virtual methods are important.)
There are no static methods in GObject, because the methods are not tied to a class as closely as they are in real object-oriented languages. Just create a top level method in your library:
void lib_something_common(params);
Finally, all of the above applies to interfaces. The interfaces work exactly the same as for the end user; they use the same
void lib_iface_method(LibIface *iface, params);
approach to the method call, the same rules and the same casting LIB_IFACE()
and LIB_IS_IFACE()
auxiliary macros.
Hope this helps! Any further explanation would need to clarify how to create the GObject, which I tried to leave out of the scope of this answer for simplicity, but it's good to know anyway.
source to share