How to define and implement interfaces

Defining interfaces

The theory behind how GObject interfaces work is given in the section called “Non-instantiable classed types: interfaces”; this section covers how to define and implement an interface.

The first step is to get the header right. This interface defines two methods:

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
/*
 * Copyright/Licensing information.
 */

#ifndef __VIEWER_EDITABLE_H__
#define __VIEWER_EDITABLE_H__

#include <glib-object.h>

G_BEGIN_DECLS

#define VIEWER_TYPE_EDITABLE viewer_editable_get_type ()
G_DECLARE_INTERFACE (ViewerEditable, viewer_editable, VIEWER, EDITABLE, GObject)

struct _ViewerEditableInterface
{
  GTypeInterface parent_iface;

  void (*save) (ViewerEditable  *self,
                GError         **error);
  void (*undo) (ViewerEditable  *self,
                guint            n_steps);
  void (*redo) (ViewerEditable  *self,
                guint            n_steps);
};

void viewer_editable_save (ViewerEditable  *self,
                           GError         **error);
void viewer_editable_undo (ViewerEditable  *self,
                           guint            n_steps);
void viewer_editable_redo (ViewerEditable  *self,
                           guint            n_steps);

G_END_DECLS

#endif /* __VIEWER_EDITABLE_H__ */

This code is the same as the code for a normal GType which derives from a GObject except for a few details:

  • The _GET_CLASS function is called _GET_IFACE (and is defined by G_DECLARE_INTERFACE).

  • The instance type, ViewerEditable, is not fully defined: it is used merely as an abstract type which represents an instance of whatever object which implements the interface.

  • The parent of the ViewerEditableInterface is GTypeInterface, not GObjectClass.

The implementation of the ViewerEditable type itself is trivial:

  • G_DEFINE_INTERFACE creates a viewer_editable_get_type function which registers the type in the type system. The third argument is used to define a prerequisite interface (which we'll talk about more later). Just pass 0 for this argument when an interface has no prerequisite.

  • viewer_editable_default_init is expected to register the interface's signals if there are any (we will see a bit later how to use them).

  • The interface methods viewer_editable_save, viewer_editable_undo and viewer_editable_redo dereference the interface structure to access its associated interface function and call it.

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
G_DEFINE_INTERFACE (ViewerEditable, viewer_editable, G_TYPE_OBJECT);

static void
viewer_editable_default_init (ViewerEditableInterface *iface)
{
    /* add properties and signals to the interface here */
}

void
viewer_editable_save (ViewerEditable  *self,
                      GError         **error)
{
  ViewerEditableInterface *iface;

  g_return_if_fail (VIEWER_IS_EDITABLE (self));
  g_return_if_fail (error == NULL || *error == NULL);

  iface = VIEWER_EDITABLE_GET_IFACE (self);
  g_return_if_fail (iface->save != NULL);
  iface->save (self, error);
}

void
viewer_editable_undo (ViewerEditable *self,
                      guint           n_steps)
{
  ViewerEditableInterface *iface;

  g_return_if_fail (VIEWER_IS_EDITABLE (self));

  iface = VIEWER_EDITABLE_GET_IFACE (self);
  g_return_if_fail (iface->undo != NULL);
  iface->undo (self, n_steps);
}

void
viewer_editable_redo (ViewerEditable *self,
                      guint           n_steps)
{
  ViewerEditableInterface *iface;

  g_return_if_fail (VIEWER_IS_EDITABLE (self));

  iface = VIEWER_EDITABLE_GET_IFACE (self);
  g_return_if_fail (iface->redo != NULL);
  iface->redo (self, n_steps);
}