GObject Basic Concepts
A brief tutorial on how to write a GObject
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 (0for integer,0.0for doubles,NULLfor 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 typeNcmModelCtrlare 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 withG_PARAM_CONSTRUCTorG_PARAM_CONSTRUCT_ONLYoptions. If the user calls thenc_object_name_newfunction 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\_CONSTRUCTproperties are always initialized by nc_object_name_set_property calls. The\_CONSTRUCT_ONLYvariables are only set during this phase and cannot be set afterwards. Any other properties passed in the _new function, which are notCONSTRUCT, 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-CONSTRUCTproperties._nc_object_name_constructed: this function is called after the_CONSTRUCTproperties are set. This function is sometimes necessary since in some cases we must do some work after knowing the values of the_CONSTRUCTvariables._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_clearfunctions._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_reffunction the object’sref_countis 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_freeand_clearfunctions is that_clearwill check if the variable isNULL. If so,_cleardoes nothing, otherwise it decreases ref_count by one and sets the variable toNULL. 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
/**
* NcObjectName:
*
* Object short description
*
* 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);
}