This site has been retired. For up to date information, see handbook.gnome.org or gitlab.gnome.org.


[Home] [TitleIndex] [WordIndex

Subclassing GObject

This page describes how to create a subclass of GObject in C, using best standard practices.

Preconditions

To use the instructions in this page, you need to be using GLib 2.44 or later (or git master as of February 2015). Additionally, the class that you are subclassing must support GLib's new auto-cleanup functionality (as a requirement of using the type declaration macros below). All classes in GLib and Gtk have this support.

Naming and conventions

In general, the program or library that you are working on should have a namespace that is used as the first part of the name of all of the classes that it contains. Gtk uses the prefix Gtk, GLib uses the prefix G, and so on.

Following this, you'll need a descriptive name for what the class is doing. This is typically a noun.

In our example, we'll assume that our namespace is MyApp and we'll make a class called MyAppWindow which is a subclass of GtkApplicationWindow.

When you define a class, it is expected that you will also define some additional standard macros that are used with that class. These macros are available for all object types. In our case these would be:

Most of the above macros will be written for you automatically if you follow the templates below.

File names

Nominally, each subclass is contained in its own separate .c file with an associated .h file. The .c file contains the implementation of the class and the .h describes the public interface of the class that is visible to its users.

Filenames are typically in lowercase and separated by dashes (-) in the places that you'd have underscores in the function names. In our example, we would expect to have files named my-app-window.c and my-app-window.h.

Two styles for subclassing

There are two styles of creating subclasses.

The difference between them is if you intend for your subclass to be further subclassed.

If your subclass will not be further subclassed then it is said to be "final". If your subclass wants to support further subclassing then it is "derivable".

A final class has several advantages:

A derivable class has the advantage of being subclassable.

In general, it is better to make a class final unless you know that it needs to be derivable. It is always possible to make a final class derivable later without breaking API or ABI compatibility, but not the other way around.

Final class

Here is an example of creating our subclass as a final class.

my-app-window.h:

/* Copyright YEAR Copyright Holder
 *
 * Licence text goes here
 *
 * Author: you <your@email>
 */

#ifndef _my_app_window_h_
#define _my_app_window_h_

G_BEGIN_DECLS

#define MY_APP_TYPE_WINDOW (my_app_window_get_type ())
G_DECLARE_FINAL_TYPE(MyAppWindow, my_app_window, MY_APP, WINDOW, GtkApplicationWindow)

MyAppWindow *          my_app_window_new            (void);

G_END_DECLS

#endif /* _my_app_window_h_ */

my-app-window.c:

/* Copyright YEAR Copyright Holder
 *
 * Licence text goes here
 *
 * Author: you <your@email>
 */

#include "my-app-window.h"

struct _MyAppWindow
{
  GtkApplicationWindow parent_instance;

  // instance variables for subclass go here
};

G_DEFINE_TYPE(MyAppWindow, my_app_window, GTK_TYPE_APPLICATION_WINDOW)

static void
my_app_window_init (MyAppWindow *window)
{
  // initialisation goes here
}

static void
my_app_window_class_init (MyAppWindowClass *class)
{
  // virtual function overrides go here
  // property and signal definitions go here
}

MyAppWindow *
my_app_window_new (void)
{
  return g_object_new (MY_APP_TYPE_WINDOW, NULL);
}

Some notes about the above:

Derivable class

Here is an example of creating a derivable class. Let's create a GtkLabel subclass called MyAppLabel.

my-app-label.h:

/* Copyright YEAR Copyright Holder
 *
 * Licence text goes here
 *
 * Author: you <your@email>
 */

#ifndef _my_app_label_h_
#define _my_app_label_h_

G_BEGIN_DECLS

#define MY_APP_TYPE_LABEL (my_app_label_get_type ())
G_DECLARE_DERIVABLE_TYPE(MyAppLabel, my_app_label, MY_APP, LABEL, GtkApplicationLabel)

struct _MyAppLabelClass {
  GtkLabelClass parent_class;

  gchar * (* transform_text)   (MyAppLabel  *label,
                                const gchar *text);

  /*< private >*/
  gpointer _padding[10];
};

MyAppLabel *          my_app_label_new            (void);

void                  my_app_label_set_text       (MyAppLabel  *label,
                                                   const gchar *text);

G_END_DECLS

#endif /* _my_app_label_h_ */

my-app-label.c:

/* Copyright YEAR Copyright Holder
 *
 * Licence text goes here
 *
 * Author: you <your@email>
 */

#include "my-app-label.h"

typedef struct
{
  guint num_changes;
} MyAppLabelPrivate;

G_DEFINE_TYPE_WITH_PRIVATE (MyAppLabel, my_app_label, GTK_TYPE_APPLICATION_LABEL)

static gchar *
my_app_label_real_transform_text (MyAppLabel  *label,
                                  const gchar *text,
                                  guint        change_nr)
{
  return g_strdup_printf ("%d %s", change_nr, text);
}

static void
my_app_label_init (MyAppLabel *label)
{
  // initialisation goes here
}

static void
my_app_label_class_init (MyAppLabelClass *class)
{
  class->transform_text = my_app_label_real_transform_text;
}

void
my_app_label_transform_text (MyAppLabel  *label,
                             const gchar *text,
                             guint        change_nr)
{
  g_return_if_fail (G_IS_MY_APP_LABEL (label));

  return MY_APP_LABEL_GET_CLASS(label)->transform_text (label, text, change_nr);
}

void
my_app_label_set_text (MyAppLabel  *label,
                       const gchar *text)
{
  MyAppLabelPrivate *priv = my_app_label_get_instance_private (label);
  guint change_nr = priv->num_changes++;
  gchar *transformed;

  transformed = my_app_label_transform_text (label, text, change_nr);
  gtk_label_set_text (GTK_LABEL (label), transformed);
  g_free (transformed);
}

MyAppLabel *
my_app_label_new (void)
{
  return g_object_new (MY_APP_TYPE_LABEL, NULL);
}

As compared with the other example, there are a few noticeable changes:


2024-10-23 11:12