The following is an article written by instructor song N years ago.
Original link: http://www.ibm.com/developerworks/cn/linux/l-gobject/index.html
The following is an excerpt. I would like to thank the author of this article ~~~~~
Bytes ------------------------------------------------------------------------------------------------------------------------------
Gobject Object System
Gobject Object System
Introduction: in short, the gobject Object System is based on glib and completed in C language, cross-platform features, flexible, scalable, and easily mapped to object-oriented frameworks in other languages. If you are a persistent follower of C language, you have no reason not to study it.
Preface
Most modern computer languages have their own types and object systems with algorithm structures attached. Just like the basic types and algorithm structures provided by glib (such as linked lists and hash tables, the gobject object system provides a flexible, scalable, and easily mapped (to other languages) Object-oriented C language framework. Its essence can be summarized:
A general type system is used to register any lightweight, single-inherited interface that can push and export any depth of structure type. It takes care of the customization, initialization, and memory management of composite objects, class Structure, maintain the parent-child relationship of objects, and process dynamic implementations of these types. That is to say, these types of implementations are reset and uninstalled at runtime;
An implementation set of basic types, such as integer, enumeration, and structural;
An example of implementation of basic object types on the basic object system-the basic type of gobject;
A signal system allows users to flexibly customize virtual or overloaded object methods, and can act as a very effective notification mechanism;
A Scalable parameter/variable system that supports all basic types that can be used to process object attributes or other parameterized types.
Type (gtype) and object (gobject)
The most distinctive feature of glib is its object system-gobject system, which is a single inherited C language object-oriented framework based on gtype.
Gtype is the type authentication and Management System for glib runtime. Gtype API is the basic system of gobject, so understanding gtype is the key to understanding gobject. Gtype provides technical implementation for registering and managing all basic data types, user-defined objects, and interface types. (Note: before using any gtype or gobject function, you must run the g_type_init () function to initialize the type system .)
For the purpose of customizing and registering types, all types must be static or dynamic. Static types can never be loaded or detached at runtime, while dynamic types can. The static type is created by g_type_register_static () and the special information of the type is obtained through the gtypeinfo structure. The dynamic type is created by g_type_register_dynamic (). gtypein in structure is used to replace gtypeinfo, and g_type_plugin _ * () series APIs are also included. These registration functions run only once to obtain the type identifiers of the classes they return.
You can also use g_type_register_fundamental to register the basic type. It requires both gtypeinfo and gtypefundamentalinfo structures. In fact, this is not necessary in most cases, because the basic types pre-defined by the system are better than those defined by the user.
(This article focuses on creating and using static types .)
Object Definition
In the gobject system, an object consists of three parts:
ID of the object (unique, unsigned long integer, all the common identifiers of such objects );
Class Structure of an object (unique and structured, owned by all instances of the object );
Instance of the object (multiple, structural, specific implementation of the object ).
What are gobject-based objects? The following is the definition code of boy, a simple object based on gobject:
/* Boy. H */
# Ifndef _ boy_h __
# DEFINE _ boy_h __
# Include <glib-object.h>
# Define boy_type (boy_get_type ())
# Define boy (OBJ) (g_type_check_instance_cast (OBJ), boy_type, boy ))
Typedef struct _ boy;
Typedef struct _ boyclass;
Struct _ boy {
Gobject parent;
//
Gint age;
Gchar * Name;
Void (* cry) (void );
};
Struct _ boyclass {
Gobjectclass parent_class;
//
Void (* boy_born) (void );
};
Gtype boy_get_type (void );
Boy * boy_new (void );
Int boy_get_age (boy * Boy );
Void boy_set_age (boy * Boy, int age );
Char * boy_get_name (boy * Boy );
Void boy_set_name (boy * Boy, char * Name );
Boy * boy_new_with_name (gchar * Name );
Boy * boy_new_with_age (Gint age );
Boy * boy_new_with_name_and_age (gchar * Name, Gint age );
Void boy_info (boy * Boy );
# Endif/* _ boy_h __*/
This is a typical header file definition in C language, including compilation preprocessing, macro definition, data structure definition, and function declaration. The first thing to look at is two data structure objects: Boy and boyclass,
The schema type _ boy is an instance of the boy object. That is to say, every time we create a boy object, we also create a boy structure. The parent in the boy object indicates the parent class of this object. The common root of all objects in the gobject system is a gobject class, so this is required. Other members can be public, here, the age that represents the age, the name of the name, and the function pointer cry that represents the method. External code can operate or reference them.
Structure Type _ boyclass is the class structure of the boy object, which is shared by all the boy object instances. In boyclass, parent_class is gobjectclass, which is the same as gobject is the root of all objects. gobejctclass is the root of the class structure of all objects. In boyclass, we also define a function pointer boy_born. That is to say, this function pointer is also shared by all boy object instances and can be called by all boy instances. Similarly, if necessary, you can also define other data members in the class structure.
The remaining function definitions include three types: boy_get_type, which is required to obtain the boy object type ID; boy_new and boy_new_with _*, this is a clear way to create objects. Of course you can also use the g_object_new function to create objects; the third is the boy_get _ * And boy_set _ * functions that set or obtain the value of the member of the boy object property _*. Under normal circumstances, these three functions are required by an object, and the other function boy_info is used to display the current state of this object.
Macros are widely used and important in the gobject system. Two key macros are defined here. boy_type macro encapsulates the boy_get_type function, which can directly obtain and replace the IDs of boy objects; the boy (OBJ) macro is the re-encapsulation of the g_type_check_instance_cast macro to forcibly convert a gobject to a boy object, which is critical and frequently used in Object Inheritance.
Object implementation
The following code defines the above boy object:
/* Boy. C */
# Include "Boy. H"
Enum {boy_born, last_signal };
Static Gint boy_signals [last_signal] = {0 };
Static void boy_cry (void );
Static void boy_born (void );
Static void boy_init (boy * Boy );
Static void boy_class_init (boyclass * boyclass );
Gtype boy_get_type (void)
{
Static gtype boy_type = 0;
If (! Boy_type)
{
Static const gtypeinfo boy_info = {
Sizeof (boyclass ),
Null, null,
(Gclassinitfunc) boy_class_init,
Null, null,
Sizeof (boy ),
0,
(Ginstanceinitfunc) boy_init
};
Boy_type = g_type_register_static (g_type_object, "boy", & boy_info, 0 );
}
Return boy_type;
}
Static void boy_init (boy * Boy)
{
Boy-> age = 0;
Boy-> name = "NONE ";
Boy-> cry = boy_cry;
}
Static void boy_class_init (boyclass * boyclass)
{
Boyclass-> boy_born = boy_born;
Boy_signals [boy_born] = g_signal_new ("boy_born ",
Boy_type,
G_signal_run_first,
G_struct_offset (boyclass, boy_born ),
Null, null,
G_cclosure_marshal_void _ void,
G_type_none, 0, null );
}
Boy * boy_new (void)
{
Boy * boy;
Boy = g_object_new (boy_type, null );
G_signal_emit (boy, boy_signals [boy_born], 0 );
Return boy;
}
Int boy_get_age (boy * Boy)
{
Return boy-> age;
}
Void boy_set_age (boy * Boy, int age)
{
Boy-> age = age;
}
Char * boy_get_name (boy * Boy)
{
Return boy-> name;
}
Void boy_set_name (boy * Boy, char * name)
{
Boy-> name = Name;
}
Boy * boy_new_with_name (gchar * name)
{
Boy * boy;
Boy = boy_new ();
Boy_set_name (boy, name );
Return boy;
}
Boy * boy_new_with_age (Gint age)
{
Boy * boy;
Boy = boy_new ();
Boy_set_age (boy, age );
Return boy;
}
Boy * boy_new_with_name_and_age (gchar * Name, Gint age)
{
Boy * boy;
Boy = boy_new ();
Boy_set_name (boy, name );
Boy_set_age (boy, age );
Return boy;
}
Static void boy_cry (void)
{
G_print ("the boy is crying... \ n ");
}
Static void boy_born (void)
{
G_print ("message: a boy was born. \ n ");
}
Void boy_info (boy * Boy)
{
G_print ("the boy name is % s \ n", boy-> name );
G_print ("the boy age is % d \ n", boy-> age );
}
In this Code, there is a key function for implementing the boy object, which is not included in the definition of the boy object and is not necessary. There are two initialization functions, boy_init and boy_class_init, which are used to initialize the instance structure and class structure respectively. They are not obviously called in the Code. The key is to convert them into address pointers using macros, assign values to the gtypeinfo structure, and then handle them by the gtype system, it is also necessary to define them as static.
The gtypeinfo structure defines the object type information, including the following:
Including the length of the class structure (required, that is, the length of the boyclass structure we define );
Base initialization function (optional );
Base finalization function (optional );
(The above two functions can be used to allocate and release the memory used by the object. gbaseinitfunc and gbasefinalizefunc are used to convert the memory to a pointer. This is not used in this example, so it is set to null .)
Class initialization function (the boy_class_init function here, which is converted using a gclassinit Macro. Optional. It is only used for classes and instance types );
Class termination function (optional );
Instance initialization function (optional, that is, the boy_init function here );
The last member is the gtype variable table (optional ).
After the gtypeinfo structure is defined, you can use the g_type_register_static function to register the object type.
The g_type_register_static function is used to register the object type. The first parameter indicates the object type of the parent class of the object. Here we use g_type_object. This macro is used to represent the parent class of the gobject; the second parameter indicates the name of this object. Here it is "boy"; the third parameter is the gtypeinfo structure pointer of this object, which is assigned as & boyinfo; the fourth parameter is the integer ID of the object returned after the object is successfully registered.
The g_object_new function is used to create an object based on g_object. It can have multiple parameters. The first parameter is the registered object ID mentioned above; the second parameter indicates the number of subsequent parameters. If it is 0, there is no third parameter. The start type of the third parameter is gparameter, which is also a structure type and is defined:
Struct gparameter {
Const gchar * Name;
Gvalue value;
};
Gvalue is a unified definition of variable types. It is a basic variable container structure used to encapsulate variable values and variable types. It can be used in the gvalue section of the gobject document.
Definition and Application of Signals
In the gobject system, a signal is a means of customizing object behavior and a notification mechanism for multiple purposes. Beginners may first come into contact with the concept of signal in GTK +. In fact, it can also be used normally in general character interface programming, which may not be thought of by many beginners.
An object can have no or multiple signals. When one or more signals exist, the definition of the signal name is essential. In this case, the function of the C language Enumeration type is highlighted, last_signal is a good programming style to represent the last signal (unimplemented signal. Here, a signal boy_born is defined for the boy object, which is issued when the object is created, indicating that the boy object was born.
At the same time, you also need to define a static array of integer pointers to save the Signal Identification, so that it can be used for Signal Processing in the next step.
The class structure of the object is shared by all object instances. We define the signal in the class structure of the object, so that the signal is also shared by all object instances, instances of any object can process signals. Therefore, it is necessary to create a letter in the class initialization function (this may be the original intention of the gobject designer ). The g_signal_new function is used to create a new signal. Its detailed usage can be found in the gobject API documentation. After the signal is successfully created, a signal ID is returned. In this way, you can use the signal transmitting function g_signal_emit to send signals to the instance of the specified object to execute the corresponding functions.
In this example, if a new boy object is created, a boy_born signal is sent, and the boy_born function defined by us is executed, a line of "message: a boy was born. "Information.
Object Attributes and Methods
All attributes and methods of the object instance are generally defined in the instance structure of the object. The attribute is defined as a variable or a variable pointer, and the method is defined as a function pointer, we must define the function as the static type. It is valid only when the function pointer is assigned a value.
Object Inheritance
The following is the implementation of man objects inherited from the boy object. A property job and a method bye are added to the man object.
# Ifndef _ man_h __
# DEFINE _ man_h __
# Include "Boy. H"
# Define man_type (man_get_type ())
# Define man (OBJ) (g_type_check_instance_cast (OBJ), man_type, man ))
Typedef struct _ man;
Typedef struct _ manclass;
Struct _ man {
Boy parent;
Char * job;
Void (* Bye) (void );
};
Struct _ manclass {
Boyclass parent_class;
};
Gtype man_get_type (void );
Man * man_new (void );
Gchar * man_get_gob (MAN * Man );
Void man_set_job (MAN * Man, gchar * job );
Man * man_new_with_name_age_and_job (gchar * Name, Gint age, gchar * job );
Void man_info (MAN * Man );
# Endif/_ man_h __
/* Man. C */
# Include "man. H"
Static void man_bye (void );
Static void man_init (MAN * Man );
Static void man_class_init (MAN * Man );
Gtype man_get_type (void)
{
Static gtype man_type = 0;
If (! Man_type)
{
Static const gtypeinfo man_info = {
Sizeof (manclass ),
Null, null,
(Gclassinitfunc) man_class_init,
Null, null,
Sizeof (man ),
0,
(Ginstanceinitfunc) man_init
};
Man_type = g_type_register_static (boy_type, "man", & man_info, 0 );
}
Return man_type;
}
Static void man_init (MAN * Man)
{
Man-> job = "NONE ";
Man-> byE = man_bye;
}
Static void man_class_init (MAN * Man)
{
}
Man * man_new (void)
{
Man * Man;
Man = g_object_new (man_type, 0 );
Return man;
}
Gchar * man_get_gob (MAN * Man)
{
Return man-> job;
}
Void man_set_job (MAN * Man, gchar * job)
{
Man-> job = job;
}
Man * man_new_with_name_age_and_job (gchar * Name, Gint age, gchar * job)
{
Man * Man;
Man = man_new ();
Boy_set_name (boy (man), name );
Boy_set_age (boy (man), age );
Man_set_job (man, job );
Return man;
}
Static void man_bye (void)
{
G_print ("Goodbye everyone! \ N ");
}
Void man_info (MAN * Man)
{
G_print ("the man name is % s \ n", boy (man)-> name );
G_print ("the man age is % d \ n", boy (man)-> age );
G_print ("the man job is % s \ n", man-> job );
}
The key is to define the parent object instance as boy and the parent class as boyclass. When registering this object, set the parent object type to boy_type, when setting object attributes, if the attributes of the parent object are required to be forcibly converted, if the object's name attribute is obtained, boy (OBJ)-> name must be used, because man does not have the name attribute, but its parent object is boy, use the boy macro to forcibly set it to a boy-type object.
Test the Defined Object
# Include <glib. h>
# Include "Boy. H"
# Include "man. H"
Int main (INT argc, char * argv [])
{
Boy * Tom, * Peter;
Man * green, * Brown;
G_type_init (); // note that the initialization type system is required.
Tom = boy_new_with_name ("Tom ");
Tom-> cry ();
Boy_info (Tom );
Peter = boy_new_with_name_and_age ("Peter", 10 );
Peter-> cry ();
Boy_info (Peter );
Green = man_new ();
Boy_set_name (boy (green), "green ");
// Set the name attribute of the man object to use its parent object boy.
Boy_set_age (boy (green), 28 );
Man_set_job (Green, "doctor ");
Green-> Bye ();
Man_info (green );
Brown = man_new_with_name_age_and_job ("brown", 30, "teacher ");
Brown-> Bye ();
Man_info (brown );
}
The MAKEFILE file is as follows:
Cc = gcc
ALL:
$ (CC)-C boy. c 'pkg-config -- cflags glib-2.0 gobject-2.0'
$ (CC)-C man. c 'pkg-config -- cflags glib-2.0 gobject-2.0'
$ (CC)-C main. c 'pkg-config -- cflags glib-2.0 gobject-2.0'
$ (CC)-O simple boy. O man. O main. O 'pkg-config -- libs glib-2.0 gobject-2.0'
Run the make command to compile the program. After compilation, run./simple to run the test program. The output result is as follows:
Message: a boy was born.
The boy is crying ......
The boy name is Tom
The boy age is 0
Message: a boy was born.
The boy is crying ......
The boy name is Peter
The boy age is 10
Goodbye everyone!
The man name is green
The man age is 28
The man job is doctor
Goodbye everyone!
The man name is Brown
The man age is 30
The man job is teacher
Makefile uses the 'pkg-config-cflags-libs gobject-2.0 'and treats threads (gthreads), plug-ins (gmoudle), and object systems (gobjects) differently in glib, note the corresponding parameters when compiling.
This article briefly introduces how to define and implement gobject. There are many related content in the gobject system, such as enumeration and flags types; gboxed, it is a mechanism registered in the gtype system to encapsulate the C language structure type that is not transparent. Many objects use parameter objects of the C structure type, and users do not need to understand the internal definition of their structure, that is, they are not transparent, gboxed is the mechanism for implementing this function. standard parameters and variable types (standard parameter and value types) are all developed in C language, it is the key to understanding and understanding gobject in depth.
Through the above Code implementation, we can also see that the GTK +/gnome Development Environment Based on glib has a unique programming style and unique development ideas. This will be more experienced in long-term programming practices.
With the foundation of the gobject system, GTK + cleverly encapsulates widgets in the X Window environment, which makes it easier to develop GUI applications on the Linux platform, faster.
The above code is compiled in the RedHat 8.0 Linux platform and glib2.2.1 environment.