## 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);
    }