Interfaces usage in library is like class usage. We need to define a interface struct, but no object struct is needed:
NOTE: PLEASE READ ALL COMMENT CAREFULLY.
1 2 3 4 5 |
typedef struct _FakeIServer FakeIServer; /* dummy object */ typedef struct _FakeIServerInterface { GTypeInterface parent; void (*response)(FakeIServer *instance); } FakeIServerInterface; |
Then we register the interface using g_type_register_static()
with G_TYPE_INTERFACE
as first parameter. For interfaces, we only need to assign base_init()
and base_finalize()
callbacks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
static void fake_iserver_base_init(gpointer g_class) { static gboolean is_initialized = FALSE; if (!is_initialized) { /* add properties and signals to the interface here */ is_initialized = TRUE; } } GType fake_iserver_get_type() { static GType type_id = 0; if (type_id == 0) { static const GTypeInfo interface_info = { sizeof(FakeIServerInterface), /* class_size */ fake_iserver_base_init, /* base_init */ NULL, /* base_finalize */ }; type_id = g_type_register_static( G_TYPE_INTERFACE, "FakeIServerInterface", &interface_info, 0); } return type_id; } |
As described in the official document, we should allocate dynamic memebers of class struct in base_init()
. Otherwise, all copies of the class struct share only one copy of dynamic members. This leads to problems.
Let’s define the type which implements the interface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// fakedesktop.h #ifndef FAKE_DESKTOP_H_ #define FAKE_DESKTOP_H_ #include <glib-object.h> #define FAKE_TYPE_DESKTOP ( fake_desktop_get_type() ) #define FAKE_DESKTOP(obj) \ ( G_TYPE_CHECK_INSTANCE_CAST((obj), FAKE_TYPE_DESKTOP, FakeDesktop) ) #define FAKE_IS_DESKTOP(obj) \ ( G_TYPE_CHECK_INSTANCE_TYPE((obj), FAKE_TYPE_DESKTOP) ) #define FAKE_DESKTOP_CLASS(cls) \ ( G_TYPE_CHECK_CLASS_CAST((cls), FAKE_TYPE_DESKTOP, FakeDesktopClass) ) #define FAKE_IS_DESKTOP_CLASS(cls) \ ( G_TYPE_CHECK_CLASS_TYPE((cls), FAKE_TYPE_DESKTOP) ) #define FAKE_DESKTOP_GET_CLASS(obj) \ ( G_TYPE_INSTANCE_GET_CLASS((obj), FAKE_TYPE_DESKTOP, FakeDesktopClass ) ) /* Base object struct */ typedef struct _FakeDesktop { /* GObject as the first field */ GObject parent; } FakeDesktop; /* Base class struct */ typedef struct _FakeDesktopClass { /* GObjectClass as the first field */ GObjectClass parent; } FakeDesktopClass; /* type method */ GType fake_desktop_get_type(); #endif /* FAKE_DESKTOP_H_ */ |
Note the naming convention I used here. Our FakeDesktop
class will implement the FakeIServer
interface and another FakeIClient
interface. This time do not use corresponding interface struct as the first members of FakeDesktop
and FakeDesktopClass
. Interface info will be added dynamically when initialize a real instance of FakeDesktop
. Let’s move to the *.c code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
// fakedesktop.c #include "fakedesktop.h" #include "fakeiface.h" void fake_desktop_request(FakeIClient *instance) { g_print("Invoking fake_desktop_request()\n"); } void fake_desktop_response(FakeIServer *instance) { g_print("Invoking fake_desktop_response()\n"); } static void fake_desktop_class_init(FakeDesktopClass *klass, gpointer data) { } static void fake_desktop_instance_init(FakeDesktop *instance, gpointer data) { } static void fake_desktop_interface_init_iclient(FakeIClientInterface* iface, gpointer iface_data) { iface->request = fake_desktop_request; } static void fake_desktop_interface_init_iserver(FakeIServerInterface* iface, gpointer iface_data) { iface->response = fake_desktop_response; } GType fake_desktop_get_type() { static GType type_id = 0; if (type_id == 0) { static const GTypeInfo type_info = { sizeof(FakeDesktopClass), /* class_size */ NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc)fake_desktop_class_init, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ sizeof(FakeDesktop), /* instance_size */ 0, /* n_preallocs */ (GInstanceInitFunc)fake_desktop_instance_init, /* instance_init */ NULL /* value_table */ }; type_id = g_type_register_static(G_TYPE_OBJECT, "FakeDesktopClass", &type_info, 0); /* add interface */ GInterfaceInfo interface_info_iclient = { (GInterfaceInitFunc)fake_desktop_interface_init_iclient, /* interface_init */ NULL, /* interface_finalize */ NULL, /* interface_data */ }; GInterfaceInfo interface_info_iserver = { (GInterfaceInitFunc)fake_desktop_interface_init_iserver, /* interface_init */ NULL, /* interface_finalize */ NULL, /* interface_data */ }; g_type_add_interface_static(type_id, FAKE_TYPE_ICLIENT, &interface_info_iclient); g_type_add_interface_static(type_id, FAKE_TYPE_ISERVER, &interface_info_iserver); } return type_id; } |
Note the g_type_add_interface_static()
function call to add interface info. The interface info is defined in a GInterfaceInfo
struct. We just make use of the interface_init()
callback. In it, we assign function pointers of corresponding interface to our implementation function. We can add multiple interface infos to implement them.
Finally, the test code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
// main.c #include "fakeiface.h" #include "fakedesktop.h" #include "fakelaptop.h" #include <glib-object.h> void my_dump_type(GType type_id) { g_print("Type id: %d\n", type_id); g_print("Type name: %s\n", g_type_name(type_id)); g_print("Is fundamental? %s\n", G_TYPE_IS_FUNDAMENTAL(type_id) ? "yes" : "no"); g_print("Is derived? %s\n", G_TYPE_IS_DERIVED(type_id) ? "yes" : "no"); g_print("Is interface? %s\n", G_TYPE_IS_INTERFACE(type_id) ? "yes" : "no"); g_print("Is classed? %s\n", G_TYPE_IS_CLASSED(type_id) ? "yes" : "no"); g_print("Is instantiatable? %s\n", G_TYPE_IS_INSTANTIATABLE(type_id) ? "yes" : "no"); g_print("Is derivable? %s\n", G_TYPE_IS_DERIVABLE(type_id) ? "yes" : "no"); g_print("Is deep derivable? %s\n", G_TYPE_IS_DEEP_DERIVABLE(type_id) ? "yes" : "no"); g_print("Is abstract? %s\n", G_TYPE_IS_ABSTRACT(type_id) ? "yes" : "no"); g_print("Is value abstract? %s\n", G_TYPE_IS_VALUE_ABSTRACT(type_id) ? "yes" : "no"); g_print("Is value type: %s\n", G_TYPE_IS_VALUE_TYPE(type_id) ? "yes" : "no"); g_print("Has value table: %s\n", G_TYPE_HAS_VALUE_TABLE(type_id) ? "yes" : "no"); } int main() { g_type_init(); my_dump_type(FAKE_TYPE_ICLIENT); my_dump_type(FAKE_TYPE_ISERVER); my_dump_type(FAKE_TYPE_LAPTOP); my_dump_type(FAKE_TYPE_DESKTOP); FakeLaptop *laptop = (FakeLaptop *)g_object_new(FAKE_TYPE_LAPTOP, NULL); FakeDesktop *desktop = (FakeDesktop *)g_object_new(FAKE_TYPE_DESKTOP, NULL); g_print("laptop is FakeIServer? %s\n", FAKE_IS_ISERVER(laptop) ? "yes" : "no"); g_print("laptop is FakeIClient? %s\n", FAKE_IS_ICLIENT(laptop) ? "yes" : "no"); g_print("desktop is FakeIServer? %s\n", FAKE_IS_ISERVER(desktop) ? "yes" : "no"); g_print("desktop is FakeIClient? %s\n", FAKE_IS_ICLIENT(desktop) ? "yes" : "no"); /* Polynophysm */ int i; FakeIServer *servers[2] = { (FakeIServer *)laptop, (FakeIServer *)desktop }; for (i = 0; i < 2; i++) { FakeIServer *inst = servers[i]; FakeIServerInterface *iface = FAKE_ISERVER_GET_INTERFACE(inst); if (iface) { iface->response(inst); } } FakeIClient *clients[2] = { (FakeIClient *)laptop, (FakeIClient *)desktop }; for (i = 0; i < 2; i++) { FakeIClient *inst = clients[i]; FakeIClientInterface *iface = FAKE_ICLIENT_GET_INTERFACE(inst); if (iface) { iface->request(inst); } } return 0; } |
In runtime, if your classed type implements an interface, it will be considered as the interface type (is-a).
All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject6 folder.