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

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
/* declaration in the header. */
void maman_bar_do_action (MamanBar *self, /* parameters */);

/* implementation in the source file */
void
maman_bar_do_action (MamanBar *self, /* parameters */)
{
  g_return_if_fail (MAMAN_IS_BAR (self));

  /* 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
/* declaration in maman-bar.h. */
#define MAMAN_TYPE_BAR maman_bar_get_type ()
G_DECLARE_DERIVABLE_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)

struct _MamanBarClass
{
  GObjectClass parent_class;

  /* stuff */
  void (*do_action) (MamanBar *self, /* parameters */);

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

void maman_bar_do_action (MamanBar *self, /* parameters */);

/* implementation in maman-bar.c */
void
maman_bar_do_action (MamanBar *self, /* parameters */)
{
  MamanBarClass *klass;

  g_return_if_fail (MAMAN_IS_BAR (self));

  klass = MAMAN_BAR_GET_CLASS (self);
  g_return_if_fail (klass->do_action != NULL);

  klass->do_action (self, /* parameters */);
}

The code above simply redirects the do_action 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->do_action 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
static void
maman_bar_real_do_action_two (MamanBar *self, /* parameters */)
{
  /* Default implementation for the virtual method. */
}

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

  /* merely virtual method. */
  klass->do_action_two = maman_bar_real_do_action_two;
}

void
maman_bar_do_action_one (MamanBar *self, /* parameters */)
{
  MamanBarClass *klass;

  g_return_if_fail (MAMAN_IS_BAR (self));

  klass = MAMAN_BAR_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->do_action != NULL);
  klass->do_action_one (self, /* parameters */);
}

void
maman_bar_do_action_two (MamanBar *self, /* parameters */)
{
  MamanBarClass *klass;

  g_return_if_fail (MAMAN_IS_BAR (self));

  klass = MAMAN_BAR_GET_CLASS (self);
  if (klass->do_action_two != NULL)
    klass->do_action_two (self, /* parameters */);
}

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
/* declaration in maman-bar.h. */
struct _MamanBarClass
{
  GObjectClass parent;

  /* stuff */
  void (* helper_do_specific_action) (MamanBar *self, /* parameters */);

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

void maman_bar_do_any_action (MamanBar *self, /* parameters */);

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
/* this accessor function is static: it is not exported outside of this file. */
static void 
maman_bar_do_specific_action (MamanBar *self, /* parameters */)
{
  MAMAN_BAR_GET_CLASS (self)->do_specific_action (self, /* parameters */);
}

void
maman_bar_do_any_action (MamanBar *self, /* parameters */)
{
  g_return_if_fail (MAMAN_IS_BAR (self));

  /* random code here */

  /* 
   * Try to execute the requested action. Maybe the requested action
   * cannot be implemented here. So, we delegate its implementation
   * to the child class:
   */
  maman_bar_do_specific_action (self, /* parameters */);

  /* other random code here */
}

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

1
2
3
4
5
6
7
8
9
static void
maman_bar_class_init (MamanBarClass *klass)
{
  /* pure virtual method: mandates implementation in children. */
  klass->do_specific_action_one = NULL;

  /* merely virtual method. */
  klass->do_specific_action_two = maman_bar_real_do_specific_action_two;
}

Children can then implement the subclass with code such as:

1
2
3
4
5
6
7
8
static void
maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
{
  MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass);

  /* implement pure virtual function. */
  bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one;
}