Object methods: GObject Reference Manual

## Object methods

Just as with C++, there are many different ways to define object methods and extend them: the following list and sections draw on C++ vocabulary. (Readers are expected to know basic C++ concepts. Those who have not had to write C++ code recently can refer to e.g. http://www.cplusplus.com/doc/tutorial/ to refresh their memories.)

- non-virtual public methods,

- virtual public methods and

- virtual private methods

### Non-virtual public methods

These are the simplest, providing a simple method which acts on the object. Provide a function prototype in the header and an implementation of that prototype in the source file.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /* declaration in the header. */
    void viewer_file_open (ViewerFile  *self,
                           GError     **error);

    /* implementation in the source file */
    void
    viewer_file_open (ViewerFile  *self,
                      GError     **error)
    {
      g_return_if_fail (VIEWER_IS_FILE (self));
      g_return_if_fail (error == NULL || *error == NULL);

      /* do stuff here. */
    }

### Virtual public methods

This is the preferred way to create GObjects with overridable methods:

- Define the common method and its virtual function in the class structure in the public header

- Define the common method in the header file and implement it in the source file

- Implement a base version of the virtual function in the source file and initialize the virtual function pointer to this implementation in the object’s `class_init` function; or leave it as `NULL` for a ‘pure virtual’ method which must be overridden by derived classes

- Re-implement the virtual function in each derived class which needs to override it

Note that virtual functions can only be defined if the class is derivable, declared using `G_DECLARE_DERIVABLE_TYPE` so the class structure can be defined.

    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
    /* declaration in viewer-file.h. */
    #define VIEWER_TYPE_FILE viewer_file_get_type ()
    G_DECLARE_DERIVABLE_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject)

    struct _ViewerFileClass
    {
      GObjectClass parent_class;

      /* stuff */
      void (*open) (ViewerFile  *self,
                    GError     **error);

      /* Padding to allow adding up to 12 new virtual functions without
       * breaking ABI. */
      gpointer padding[12];
    };

    void viewer_file_open (ViewerFile  *self,
                           GError     **error);

    /* implementation in viewer-file.c */
    void
    viewer_file_open (ViewerFile  *self,
                      GError     **error)
    {
      ViewerFileClass *klass;

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

      klass = VIEWER_FILE_GET_CLASS (self);
      g_return_if_fail (klass->open != NULL);

      klass->open (self, error);
    }

The code above simply redirects the `open` call to the relevant virtual function.

It is possible to provide a default implementation for this class method in the object's `class_init` function: initialize the `klass->open` field to a pointer to the actual implementation. By default, class methods that are not inherited are initialized to `NULL`, and thus are to be considered "pure virtual".

    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
    static void
    viewer_file_real_close (ViewerFile  *self,
                            GError     **error)
    {
      /* Default implementation for the virtual method. */
    }

    static void
    viewer_file_class_init (ViewerFileClass *klass)
    {
      /* this is not necessary, except for demonstration purposes.
       *
       * pure virtual method: mandates implementation in children.
       */
      klass->open = NULL;

      /* merely virtual method. */
      klass->close = viewer_file_real_close;
    }

    void
    viewer_file_open (ViewerFile  *self,
                      GError     **error)
    {
      ViewerFileClass *klass;

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

      klass = VIEWER_FILE_GET_CLASS (self);

      /* if the method is purely virtual, then it is a good idea to
       * check that it has been overridden before calling it, and,
       * depending on the intent of the class, either ignore it silently
       * or warn the user.
       */
      g_return_if_fail (klass->open != NULL);
      klass->open (self, error);
    }

    void
    viewer_file_close (ViewerFile  *self,
                       GError     **error)
    {
      ViewerFileClass *klass;

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

      klass = VIEWER_FILE_GET_CLASS (self);
      if (klass->close != NULL)
        klass->close (self, error);
    }

### Virtual private Methods

These are very similar to virtual public methods. They just don't have a public function to call directly. The header file contains only a declaration of the virtual function:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /* declaration in viewer-file.h. */
    struct _ViewerFileClass
    {
      GObjectClass parent;

      /* Public virtual method as before. */
      void     (*open)           (ViewerFile  *self,
                                  GError     **error);

      /* Private helper function to work out whether the file can be loaded via
       * memory mapped I/O, or whether it has to be read as a stream. */
      gboolean (*can_memory_map) (ViewerFile *self);

      /* Padding to allow adding up to 12 new virtual functions without
       * breaking ABI. */
      gpointer padding[12];
    };

    void viewer_file_open (ViewerFile *self, GError **error);

These virtual functions are often used to delegate part of the job to child classes:

    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
    /* this accessor function is static: it is not exported outside of this file. */
    static gboolean
    viewer_file_can_memory_map (ViewerFile *self)
    {
      return VIEWER_FILE_GET_CLASS (self)->can_memory_map (self);
    }

    void
    viewer_file_open (ViewerFile  *self,
                      GError     **error)
    {
      g_return_if_fail (VIEWER_IS_FILE (self));
      g_return_if_fail (error == NULL || *error == NULL);

      /*
       * Try to load the file using memory mapped I/O, if the implementation of the
       * class determines that is possible using its private virtual method.
       */
      if (viewer_file_can_memory_map (self))
        {
          /* Load the file using memory mapped I/O. */
        }
      else
        {
          /* Fall back to trying to load the file using streaming I/O… */
        }
    }

Again, it is possible to provide a default implementation for this private virtual function:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    static gboolean
    viewer_file_real_can_memory_map (ViewerFile *self)
    {
      /* As an example, always return false. Or, potentially return true if the
       * file is local. */
      return FALSE;
    }

    static void
    viewer_file_class_init (ViewerFileClass *klass)
    {
      /* non-pure virtual method; does not have to be implemented in children. */
      klass->can_memory_map = viewer_file_real_can_memory_map;
    }

Derived classes can then override the method with code such as:

    1
    2
    3
    4
    5
    6
    7
    8
    static void
    viewer_audio_file_class_init (ViewerAudioFileClass *klass)
    {
      ViewerFileClass *file_class = VIEWER_FILE_CLASS (klass);

      /* implement pure virtual function. */
      file_class->can_memory_map = viewer_audio_file_can_memory_map;
    }