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 (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 typeNcmModelCtrl
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 withG_PARAM_CONSTRUCT
orG_PARAM_CONSTRUCT_ONLY
options. If the user calls thenc_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 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-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’sref_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 isNULL
. If so,_clear
does 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())
(NcObjectName, nc_object_name, NC, OBJECT_NAME, GObject)
G_DECLARE_FINAL_TYPE
/* METHODS */
*nc_object_name_new(gdouble prop1_val);
NcObjectName *nc_object_name_ref(NcObjectName *nc_object_name);
NcObjectName 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 */
(NcObjectName, nc_object_name, G_TYPE_OBJECT);
G_DEFINE_TYPE_WITH_PRIVATE
static void
(NcObjectName *obj)
nc_object_name_init {
* const self = nc_object_name_get_instance_private (obj);
NcObjectNamePrivate
->private_double = 0.0;
self}
static void
(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
_nc_object_name_set_property {
*obj = NC_OBJECT_NAME (object);
NcObjectName * const self = nc_object_name_get_instance_private (obj);
NcObjectNamePrivate
(NC_IS_OBJECT_NAME (object));
g_return_if_fail
switch (prop_id)
{
case PROP_PROP1:
->private_double = g_value_get_double (value);
selfbreak;
default:
(object, prop_id, pspec);
G_OBJECT_WARN_INVALID_PROPERTY_ID break;
}
}
static void
(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
_nc_object_name_get_property {
*obj = NC_OBJECT_NAME (object);
NcObjectName * const self = nc_object_name_get_instance_private (obj);
NcObjectNamePrivate
(NC_IS_OBJECT_NAME (object));
g_return_if_fail
switch (prop_id)
{
case PROP_PROP1:
(value, self->private_double);
g_value_set_double break;
default:
(object, prop_id, pspec);
G_OBJECT_WARN_INVALID_PROPERTY_ID break;
}
}
static void
(GObject *object)
_nc_object_name_dispose {
/* Chain up : end */
(nc_object_name_parent_class)->dispose (object);
G_OBJECT_CLASS }
static void
(GObject *object)
_nc_object_name_finalize {
/* Chain up : end */
(nc_object_name_parent_class)->finalize (object);
G_OBJECT_CLASS }
static void
(NcObjectNameClass *klass)
nc_object_name_class_init {
*object_class = G_OBJECT_CLASS (klass);
GObjectClass
->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;
object_class
(object_class,
g_object_class_install_property ,
PROP_PROP1("prop1",
g_param_spec_double ,
NULL"This is prop is a double between (0.0, 1.0), default 0.5 ",
0.0, 1.0, 0.5,
| G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
G_PARAM_READWRITE }
/* METHODS */
/**
* nc_object_name_new:
* @prop1: a value for prop1
*
* A simple constructor.
*
* Returns: (transfer full): a newly created #NcObjectName
*/
*
NcObjectName (gdouble prop1_val)
nc_object_name_new {
*nc_object_name = g_object_new (NC_TYPE_OBJECT_NAME,
NcObjectName "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 (NcObjectName *nc_object_name)
nc_object_name_ref {
return g_object_ref (nc_object_name);
}
/**
* nc_object_name_free:
* @nc_object_name: a #NcObjectName
*
* Decrease reference count by one.
*
*/
void
(NcObjectName *nc_object_name)
nc_object_name_free {
(nc_object_name);
g_object_unref }
/**
* 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
(NcObjectName **nc_object_name)
nc_object_name_clear {
(nc_object_name);
g_clear_object }