preamble
This article is available at/GObject-tutorial-beginner-02/Reading in the center, the experience is more
existprevious sectionGObject is a basic instantiable class type that serves as the base class for all types that use the GObject system, providing core object-oriented features such as inheritance, encapsulation, and polymorphism. However, we generally do not use GObject itself directly, but create new types by inheriting from GObject.
{% notel blue fa-circle-exclamation Note: %}
For the official GObject documentation:
Instantiatable classed types: objects (Instantiatable classed types: objects), which are referred to as types in this article and later.
Non-instantiatable classed types: interfaces, which are referred to as interfaces in this and later articles.
In this article we take a look at the naming conventions in the GObject type system and describe how to create a custom type.
naming convention
{% note blue fa-circle-exclamation %}
Naming conventions are used throughout the GObject type system.
{% endnote %}
First, let's start with a basic naming convention: an object name consists of a namespace (also known as a moudle) and a name. For example, GObject consists of the namespace "G" and the name "Object", GtkWidget consists of the namespace "Gtk" and the name GtkWidget consists of the namespace "Gtk" and the name "Widget". In this article, for better demonstration, we will define a new type with namespace "Paw" and name "Double" to represent a floating point data.
Okay, you now understand the basic naming conventions, so let's look at more of them:
- The type name must be at least three characters long and begin with a-z, A-Z, or
_
Beginning. - Function names use the object_method pattern: to use the object_method pattern in functions of type
Double
on the instance namedadd
function, then the function is nameddouble_save
。 - Using a prefix avoids namespace conflicts with other projects. For example, my library (or application) is named
Paw
, then prefix all function names withpaw_
. Example:paw_double_add
. A prefix should be a word, i.e. it should not contain any capital letters after the first letter. For example, it should be Exampleprefix instead of ExamplePrefix.
In addition to the above conventions, other naming conventions will be introduced in this paper and will be described in due course.
Type creation and registration
Type creation is the definition of a new GObject type, including all its data and behavior. This will involve:
- Defining Class Structures and Instance Structures
- Define type-related functions: e.g., class initialization (
class_init
), instance initialization (instance_init
) and other functions.
Type registration is the process of registering the created new GObject type with the GObject type system so that it becomes recognizable and usable by the GObject system. This will involve:
- Calls the type registration function:
g_type_register_static
maybeg_type_register_dynamic
These functions are responsible for registering newly created types in the GObject type system.
In our normal use, the creation and registration of types can usually be accomplished with some handy macros provided by GObject, such asG_DECLARE_FINAL_TYPE
cap (a poem)G_DEFINE_TYPE
etc. These convenience macros allow the user to not have to care about some of the specifics of type creation and registration.
In this article, in order to better understand the GObject type system, the process of type creation and registration without the use of convenience macros will be shown first, and then convenience macros will be introduced later.
{% notel blue fa-circle-exclamation Note: %}
There are two types in the GObject type system: static types and dynamic types.
For static types, their classes are not destroyed even after all instances are destroyed. For dynamic types, its class is destroyed when the last instance is destroyed.GObject's type is static, as are its derived types.
manually operated
Class and Instance Structures for Defining Types
The class and instance structure naming conventions are as follows:
- The class structure is named:
<Moudle><Name>Class
, such as PawDoubleClass. - The instance structure is named:
<Moudle><Name>
, such as PawDouble.
//class structure
typedef struct _PawDoubleClass PawDoubleClass;
struct _PawDoubleClass
{
GObjectClass parent_class;
};
The first member of a PawDoubleClass must be a class structure of its parent type
//instance structure
typedef struct _PawDouble PawDouble;
struct _PawDouble
{
GObject parent;
double value;
};
The first member of a PawDouble must be an instance struct of its parent type.
PawDouble has its own membersvalue
is the value of the floating-point data represented by the PawDouble type
Defining initialization functions
The naming convention for initialization functions for classes and instances is as follows:
- The class initialization function is named:
<moudle>_<name>_class_init
, such as paw_double_class_init. - The instance initialization function is named:
<moudle>_<name>_init
, such as paw_double_init.
//class constructor
static void paw_double_class_init(PawDoubleClass* class)
{
}
//instance constructor
static void paw_double_init(PawDouble* self)
{
}
Type Registration
For static types, we useg_type_register_static
Register it to GObject's type system
//file:
GType
g_type_register_static (GType parent_type,
const gchar* type_name,
const GTypeInfo* info,
GTypeFlags flags);
Among them:
- parent_type: the type of the parent class
- type_name: type name, e.g. PawDouble
- info: passes information about type initialization and destruction to the type system. the GTypeInfo structure is described below
- flags: if the type is an abstract type or an abstract value type, set their flag bits. Otherwise, set them to 0.
GTyepInfo structure:
typedef struct _GTypeInfo GTypeInfo;
struct _GTypeInfo
{
/* interface types, classed types, instantiated types */
guint16 class_size;
GBaseInitFunc base_init;
GBaseFinalizeFunc base_finalize;
/* interface types, classed types, instantiated types */
GClassInitFunc class_init;
GClassFinalizeFunc class_finalize;
gconstpointer class_data;
/* instantiated types */
guint16 instance_size;
guint16 n_preallocs;
GInstanceInitFunc instance_init;
/* value handling */
const GTypeValueTable *value_table;
};
This structure must be created before type registration, where:
-
class_size
: The size of the class. For example, the class size of the PawDouble type issizeof(PawDoubleClass)
。 -
base_init
,base_finalize
: These functions initialize/destroy dynamic members of the class. In many cases, they are not required and are assigned NULL. see details atcap (a poem)。 -
class_init
: Initialization function for a class. The user needs to assign the defined class initialization function to theclass_init
member. Following the naming convention, the class initialization function name is<moudle>_<name>_class_init
For examplepaw_double_class_init
。 -
class_finalize
: Destruction function for the class. Because the subclass type of GObject is static, it has no destruction function. It would be a good idea to add theclass_finalize
A member assignment of NULL is sufficient. -
class_data
: User-supplied data passed to the class' initialization/destruction functions. Usually assigned to NULL. -
instance_size
: The size of the instance. For example, the instance size of the PawDouble type issizeof(PawDouble)
。 -
n_preallocs
: Used to specify the number of pre-allocated instances. Since GLib 2.10, this field is ignored. -
instance_init
: Initialization functions for instances. The user needs to assign the defined instance initialization function to theinstance_init
member. Following the naming convention, the name of the instance initialization function is<moudle>_<name>_init
For examplepaw_double_init
。 -
value_table
: This is usually only useful for basic types. If the type is a descendant of GObject, the assignment is NULL.
We will write a functionpaw_double_get_type
, which is used to register a new type with the GObject type system and return the id of the registered type.
GType paw_double_get_type(void)
{
static GType type = 0;
GTypeInfo info;
if(type == 0)
{
//assign a value to something
info.class_size = sizeof(PawDoubleClass);
info.base_init = NULL;
info.base_finalize = NULL;
info.class_init = (GClassInitFunc) paw_double_class_init;
info.class_finalize = NULL;
info.class_data = NULL;
info.instance_size = sizeof(PawDouble);
info.n_preallocs = 0;
info.instance_init = (GInstanceInitFunc) paw_double_init;
info.value_table = NULL;
//Registration Type
type = g_type_register_static(G_TYPE_OBJECT, "PawDouble", &info, 0);
}
return type;
}
aggregate
The type creation and registration process described above is summarized below:
#include <>
#define PAW_TYPE_DOUBLE (paw_double_get_type())
//Define the instance structure
typedef struct _PawDouble PawDouble;
struct _PawDouble
{
GObject parent;
double value;
};
//Defining Class Structures
typedef struct _PawDoubleClass PawDoubleClass;
struct _PawDoubleClass
{
GObjectClass parent_class;
};
//class constructor
static void paw_double_class_init(PawDoubleClass* class)
{
}
//instance constructor
static void paw_double_init(PawDouble* self)
{
}
//Type of registration on first call
//Return Type
GType paw_double_get_type(void)
{
static GType type = 0;
GTypeInfo info;
if(type == 0)
{
info.class_size = sizeof(PawDoubleClass);
info.base_init = NULL;
info.base_finalize = NULL;
info.class_init = (GClassInitFunc) paw_double_class_init;
info.class_finalize = NULL;
info.class_data = NULL;
info.instance_size = sizeof(PawDouble);
info.n_preallocs = 0;
info.instance_init = (GInstanceInitFunc) paw_double_init;
info.value_table = NULL;
type = g_type_register_static(G_TYPE_OBJECT, "PawDouble", &info, 0);
}
return type;
}
int main(int argc, char **argv)
{
GType dtype;
PawDouble* d;
dtype = paw_double_get_type(); /* or dtype = PAW_TYPE_DOUBLE */
if(dtype)
g_print("Registration was a success. The type is %lx.\n", dtype);
else
g_print("Registration failed.\n");
d = g_object_new(PAW_TYPE_DOUBLE, NULL);
if(d)
g_print("Instantiation was a success. The instance address is %p.\n", d);
else
g_print("Instantiation failed.\n");
g_object_unref(d); /* Releases the object d. */
return 0;
}
-
20-28: Class Initialization Functions and Instance Initialization Functions, Parameters
class
is the class structure with the parameterself
is the instance struct. The contents of these two functions are empty in the example, but they are necessary for the registration process. -
30-52: The paw_double_get_type function, which returns the id of the PawDouble type.The naming of such functions always follows the
<moudle>_<name>_get_type
. Meanwhile, Macro<MOUDLE>_TYPE_<NAME>
(all characters are uppercase) is an alias for this function. paw_double_get_type function has a static variabletype
to store the type of the object. On the first call of this function, thetype
is zero. Then the in-function callg_type_register_static
Registers the new type into the type system. On the second and subsequent calls, the function simply returns thetype
Because static variablestype
have beeng_type_register_static
is assigned a non-zero value and it holds that value. -
39-49: Settings
info
structure and call theg_type_register_static
Register. -
54-73: Main function. Get the PawDouble type id and display it. Function
g_object_new
is used to create instances of the type.The GObject API documentation states thatg_object_new
returns a pointer to an instance of GObject, but it actually returns agpointer
。gpointer
is essentially the same asvoid*
that can be assigned to pointers of any type. Therefore, the statementd = g_object_new(PAW_TYPE_DOUBLE, NULL);
is correct. If the functiong_object_new
Returns theGObject*
, then the returned pointer needs to be type converted. After the creation is complete, we print the address of the instance. Finally, using the functiong_object_unref
Release and destroy the instance.
For the rest of the content, go to/GObject-tutorial-beginner-02/Middle Reading.