Location>code7788 >text

GObject Study Notes (II) Type Creation and Registration

Popularity:274 ℃/2024-12-09 18:14:34

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 typeDoubleon the instance namedaddfunction, then the function is nameddouble_save
  • Using a prefix avoids namespace conflicts with other projects. For example, my library (or application) is namedPaw, 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_staticmaybeg_type_register_dynamicThese 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_TYPEcap (a poem)G_DEFINE_TYPEetc. 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 membersvalueis 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_staticRegister 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_initmember. Following the naming convention, the class initialization function name is<moudle>_<name>_class_initFor 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_finalizeA 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_initmember. Following the naming convention, the name of the instance initialization function is<moudle>_<name>_initFor 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, Parametersclassis the class structure with the parameterselfis 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 variabletypeto store the type of the object. On the first call of this function, thetypeis zero. Then the in-function callg_type_register_staticRegisters the new type into the type system. On the second and subsequent calls, the function simply returns thetypeBecause static variablestypehave beeng_type_register_staticis assigned a non-zero value and it holds that value.

  • 39-49: Settingsinfostructure and call theg_type_register_staticRegister.

  • 54-73: Main function. Get the PawDouble type id and display it. Functiong_object_newis used to create instances of the type.The GObject API documentation states thatg_object_newreturns a pointer to an instance of GObject, but it actually returns agpointergpointeris 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_newReturns 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_unrefRelease and destroy the instance.

For the rest of the content, go to/GObject-tutorial-beginner-02/Middle Reading.