GObject Basic Concepts

A brief tutorial on how to write a GObject

Author

NumCosmo developers

Overview

The GLib Object System, or GObject, is a free software library providing a portable object system and transparent cross-language interoperability. GObject is designed for use both directly in C programs to provide object-oriented C-based APIs and through bindings to other languages to provide transparent cross-language interoperability, e.g. PyGObject. We present here a summarized description of this framework. For details see: GObject Manual, GObject Wikipedia.

Namespaces

The library is divided in two main name-spaces, Ncm (NumCosmoMath) where all generic mathematical and statistical objects and functions are defined, Nc (NumCosmo) where all cosmological related objects and functions are defined. All types must be labeled using the camel-case, and start with the respective name-space. For example, NcmSpline is the type used to store instances of the Spline object which lives in the NumCosmoMath name-space. Similarly, NcHICosmo is the type used for the abstract object defining homogeneous and isotropic models (HICosmo).

Methods, functions and other names

Functions and methods must be lower case and the terms separated by underscores, that is, a method of NcmSpline must be called ncm_spline_method_name. It is acceptable for the method_name to have upper case letters when necessary. For example, nc_hicosmo_Omega_m0 uses the capital Omega to avoid confusion with lowercase omega_m0, which has a different meaning in the cosmological literature.

Macros, enumerator and flags labels should be in uppercase and separated by underscores. See for example NcHICosmoImpl for a flag type. The flag type itself, NcHICosmoImpl, must be in camel-case but its labels are underscore separated uppercase.

A full example of a GObject implementation inside NumCosmo can be found here.

Object implementation

All objects must implement a set of functions, for example an object named NcObjectName must implement:

  • nc_object_name_class_init: the methods (virtual functions) and properties are defined here. This is the first function called when the object is instantiated, and it is done just once in the whole program lifetime.

  • nc_object_name_init: this is the first function to be called when (and every time) a new instance is created. This function receives an uninitialized instance struct and there one must initialize all members of the structure to null values (0 for integer, 0.0 for doubles, NULL for pointers, etc). Sometimes, when a given member of the instance structure is not related to a property, you must initialized it at this point (for example, members of the type NcmModelCtrl are usually initialized at _init).

  • _nc_object_name_set_property/_get_property: the next step of the GObject system is to call this function for every property with G_PARAM_CONSTRUCT or G_PARAM_CONSTRUCT_ONLY options. If the user calls the nc_object_name_new function with some properties set to specific values, then these values will be passed to nc_object_name_set_property. However, for those properties not passed in the nc_object_name_new function, the default values will be used. In short, the \_CONSTRUCT properties are always initialized by nc_object_name_set_property calls. The \_CONSTRUCT_ONLY variables are only set during this phase and cannot be set afterwards. Any other properties passed in the _new function, which are not CONSTRUCT, will be set only through the nc_object_name_set_property function. They will not be set to the default values, the default values are useless for non-CONSTRUCT properties.

  • _nc_object_name_constructed: this function is called after the _CONSTRUCT properties are set. This function is sometimes necessary since in some cases we must do some work after knowing the values of the _CONSTRUCT variables.

  • _nc_object_name_dispose: this function is called every time the object has its ref_count decreased to 0. It may be called more than once trying to break a reference count cycle (something out of the scope of this document, see this document for more details). That is why we must release all references to objects in _dispose using _clear functions.

  • _nc_object_name_finish: this function is called after nc_object_name_dispose. Here we must release any allocated memory not related to the GObject system.

Default methods

In NumCosmo we have the following conventions for methods that all objects must implement:

  • nc_object_name_ref: Every time we call a _ref function the object’s ref_count is increased by one.

  • nc_object_name_clear/_free: every time a _free or _clear function is called the ref_count is decreased by one. The only difference between _free and _clear functions is that _clear will check if the variable is NULL. If so, _clear does nothing, otherwise it decreases ref_count by one and sets the variable to NULL. When a reference to some object is given to you, you never know if anyone else also has a reference to it. So when you no longer need it, you just decrease the reference count by one. However, if you make a mistake and decrease the ref_count twice, you may destroy the object that could still be used by other parts of the code. That is why _clear function is so useful, when you call it a second time, the pointer will be null and _clear will do nothing.

Example

See below a simple example of NumCosmo/GObject implementation.

Header for NcObjectName

#ifndef _NC_OBJECT_NAME_H_
#define _NC_OBJECT_NAME_H_

#include <glib.h>
#include <glib-object.h>

G_BEGIN_DECLS

#define NC_TYPE_OBJECT_NAME (nc_object_name_get_type())

G_DECLARE_FINAL_TYPE(NcObjectName, nc_object_name, NC, OBJECT_NAME, GObject)

/* METHODS */
NcObjectName *nc_object_name_new(gdouble prop1_val);
NcObjectName *nc_object_name_ref(NcObjectName *nc_object_name);
void nc_object_name_free(NcObjectName *nc_object_name);
void nc_object_name_clear(NcObjectName **nc_object_name);

G_END_DECLS

#endif /* _NC_OBJECT_NAME_H_ */

Source for NcObjectName

/**
 * SECTION:nc_object_name
 * @title: NcObjectName
 * @short_description: Object short description
 * @include: nc_object_name.h
 *
 * Long description
 *
 */

#include "nc_object_name.h"

struct _NcObjectName
{
  /*< private >*/
  GObject parent_instance;
};

typedef struct _NcObjectNamePrivate
{
  gdouble private_double;
} NcObjectNamePrivate;

enum
{
  PROP_0,
  PROP_PROP1,
  PROP_SIZE,
};

/* This object is a child of GObject */
G_DEFINE_TYPE_WITH_PRIVATE (NcObjectName, nc_object_name, G_TYPE_OBJECT);

static void
nc_object_name_init (NcObjectName *obj)
{
  NcObjectNamePrivate * const self = nc_object_name_get_instance_private (obj);

  self->private_double = 0.0;
}

static void
_nc_object_name_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  NcObjectName *obj                = NC_OBJECT_NAME (object);
  NcObjectNamePrivate * const self = nc_object_name_get_instance_private (obj);

  g_return_if_fail (NC_IS_OBJECT_NAME (object));

  switch (prop_id)
  {
    case PROP_PROP1:
      self->private_double = g_value_get_double (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
_nc_object_name_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
  NcObjectName *obj                = NC_OBJECT_NAME (object);
  NcObjectNamePrivate * const self = nc_object_name_get_instance_private (obj);

  g_return_if_fail (NC_IS_OBJECT_NAME (object));

  switch (prop_id)
  {
    case PROP_PROP1:
      g_value_set_double (value, self->private_double);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
_nc_object_name_dispose (GObject *object)
{
  /* Chain up : end */
  G_OBJECT_CLASS (nc_object_name_parent_class)->dispose (object);
}

static void
_nc_object_name_finalize (GObject *object)
{
  /* Chain up : end */
  G_OBJECT_CLASS (nc_object_name_parent_class)->finalize (object);
}

static void
nc_object_name_class_init (NcObjectNameClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->set_property = &_nc_object_name_set_property;
  object_class->get_property = &_nc_object_name_get_property;
  object_class->dispose      = &_nc_object_name_dispose;
  object_class->finalize     = &_nc_object_name_finalize;

  g_object_class_install_property (object_class,
                                   PROP_PROP1,
                                   g_param_spec_double ("prop1",
                                                        NULL,
                                                        "This is prop is a double between (0.0, 1.0), default 0.5 ",
                                                        0.0, 1.0, 0.5,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
}

/* METHODS */

/**
 * nc_object_name_new:
 * @prop1: a value for prop1
 *
 * A simple constructor.
 *
 * Returns: (transfer full): a newly created #NcObjectName
 */
NcObjectName *
nc_object_name_new (gdouble prop1_val)
{
  NcObjectName *nc_object_name = g_object_new (NC_TYPE_OBJECT_NAME,
                                               "prop1", prop1_val,
                                               NULL);

  return nc_object_name;
}

/**
 * nc_object_name_ref:
 * @nc_object_name: a #NcObjectName
 *
 * Increase reference count by one.
 *
 * Returns: (transfer full): @nc_object_name.
 */
NcObjectName *
nc_object_name_ref (NcObjectName *nc_object_name)
{
  return g_object_ref (nc_object_name);
}

/**
 * nc_object_name_free:
 * @nc_object_name: a #NcObjectName
 *
 * Decrease reference count by one.
 *
 */
void
nc_object_name_free (NcObjectName *nc_object_name)
{
  g_object_unref (nc_object_name);
}

/**
 * nc_object_name_clear:
 * @nc_object_name: a #NcObjectName
 *
 * Decrease reference count by one if *@nc_object_name != NULL
 * and sets @nc_object_name to NULL.
 *
 */
void
nc_object_name_clear (NcObjectName **nc_object_name)
{
  g_clear_object (nc_object_name);
}