Charlas de GNOME Hispano: El sistema de objetos

Relator: Rodrigo Moya (rodrigo)
Moderador: Jorge Bustos (mhz)
Domingo 12 de enero de 2003, 21:30 horas UTC
irc.gnome.cl #gnome-hispano

mhz
Bueno Amigos, como cada domingo comienzan las Charlas de GNOME Hispano

aqui en su canal #gnome-hispano, en la charla de hoy 12 de Enero, veremos
 el 
Sistema  de Objetos de la Glib(GObject).
Recuerden que las preguntas deben hacermelas a mi, estas pueden realizarse
 en el momento de lo deseen.
Sin mas que agregar dejo con Uds. al Sr. Rodrigo Moya.
rodrigo
hola
el sistema de objetos de GLib que vamos a ver hoy
se cre inicialmente para GTK
la idea a la hora de crearlo era el traer, a un lenguaje no orientado a
 objetos
las cosas ms interesantes de la POO
como la herencia, el polimorfismo, etc
en aquel entonces, C++ no era un lenguaje suficientemente estandarizado

demasiado complejo y lioso en opinin de muchos desarrolladores
y a la mayor parte de los desarrolladores de GNOME no les gustaba
por tanto, se cre un sistema de objetos en GTK
para ser usado en la creacin de los distintos widgets
con el tiempo se vio que este sistema de objetos
era realmente ltil, no slo para apps GUI
si no para todo tpo de aplicaciones
y por tanto, en GLib 2.0, se movi de GTK a GLib
cuando se habla de sistema de objetos
realmente el trmino es equivocado
puesto que ms que un sistema de objetos
es un sistema dinmico de creacin y comprobacin de tipos de datos
es decir, en tiempo de ejecucin podemos crear nuevos tipos (entre ellos,
 clases)
registrarlos en el sistema
y hacerlos disponibles para el resto de la aplicacin
esto permite, tambien en tiempo de ejecucin
hacer comprobaciones de tipo
de forma que si yo paso a una funcin un puntero
no slo se comprueba que sea un puntero vlido
si no que se comprueba su tipo
para ver que es realmente lo que se espera
esto soluciona muchos problemas a la hora de desarrollar en C
donde no existe la comprobacin de tipos ms que en tiempo de compilacin

(y ni eso muchas veces)
el sistema de objetos/tipos de GLib no es m\x{00E1}s que una base de datos
 en la que se van registrando las distintas clases
En esa base de datos, se almacenan todas las propiedades asociadas a cada
 tipo registrado, informaci\x{00F3}n tal como las funciones de inicializaci\x{00F3}n
 del tipo, el tipo base del que deriva, el nombre del tipo, etc, todo ello
 identificado por un identificador \x{00FA}nico, conocido como GType.
as, cuando queremos registrar un tipo de datos nuevo
tenemos que crear un nuevo identificador (GType) para el nuevo tipo
y registrarlo en el sistema
existen dos tipos de tipos que podemos registrar
* tipos basados en clases (instanciables)
* tipos fundamentales (no instanciables)
los fundamentales no son ms que gint, gchar, etc
aunque podemos registrar nuevos tipos fundamentales, si as se desea
los tipos basados en clases son los que ms vereis en los programas GTK/GNOME

son los basados en GObject, que es la clase base de todos los objetos
para registrar un tipo de datos nuevo, primero hay que rellenar la estructura
 GTypeInfo:
struct _GTypeInfo
   {
     /* interface types, classed types, instantiated types */
     guint16                class_size;
     GBaseInitFunc          base_init;
     GBaseFinalizeFunc      base_finalize;
     /* 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;
   };
un ejemplo de uso:
if (!type) {
       static GTypeInfo info = {
         sizeof (MyObjectClass),
         (GBaseInitFunc) NULL,
         (GBaseFinalizeFunc) NULL,
         (GClassInitFunc) my_object_class_init,
         NULL, NULL,
         sizeof (MyObject),
         0,
         (GInstanceInitFunc) my_object_init
       };
       type = g_type_register_static (PARENT_TYPE, "MyObject", , 0);
     }
vamos a comentar el ejemplo
se trata de la creacin de una nueva clase llamada MyObject
para ello, lo primero que hay que hacer es definir dos estructuras:
typedef struct {
  GObject parent;
} MyObject;
typedef struct {
  GObjectClass parent_class;
} MyObjectClass
;
estas dos estructuras las crearemos para cada clase nueva que queramos
 crear
la primera (MyObject) contendr los datos de cada instancia (una copia de
 la struct por cada instancia) de la clase
la segunda (MyObjectClass) contiene los datos referente a todas las instancias
 de la clase
y normalmente slo hay una copia de ella en el programa
en ambas estructuras, el primer y nico campo es de tipo GObject*
esta es la forma de especificar la herencia de clases en GLib
mediante la inclusin, como primer campo de la estructura, del equivalente
 de la clase base
en este caso, GObject
una vez que tenemos estas estructuras
hay que registrar la clase, que normalmente se hace en la funcion nombreclase_get_type:

GType
   my_object_get_type (void)
   {
     static GType type = 0;
     if (!type) {
       static GTypeInfo info = {
         sizeof (MyObjectClass),
         (GBaseInitFunc) NULL,
         (GBaseFinalizeFunc) NULL,
         (GClassInitFunc) my_object_class_init,
         NULL, NULL,
         sizeof (MyObject),
         0,
         (GInstanceInitFunc) my_object_init
       };
       type = g_type_register_static (PARENT_TYPE, "MyObject", , 0);
     }
     return type;
   }
en esta funcin, simplemente se rellena la estructura de tipo GTypeInfo

con la info de la nueva clase (ahora veremos todos los valores)
y posteriormente se registra el tipo en el sistema de tipos de GLib
mediasnte la funcin g_type_register_static
que debera ser:
type = g_type_register_static (G_TYPE_OBJECT, "MyObject", &info, 0);

bien, vamos a ver la estructura:
sizeof (MyObjectClass) -> especifica el tamao de la estructura de la
 clase (MyObjectClass)
(GBaseInitFunc) my_object_init -> especifica la funcin de inicializacin
 de instancias de la clase
perdn, lo ltimo est mal :-)
(GBaseInitFunc) NULL,
<rodrigo>          (GBaseFinalizeFunc) NULL,
pasamos de estas dos lneas, de momento
(GClassInitFunc) my_object_class_init -> especifica la funcin de inicializacin
 de la clase
NULL, NULL, -> pasamos de ellos
 sizeof (MyObject) -> especifica el tamao de la estructura de instancia
 (MyObject)
0 -> pasamos
(GInstanceInitFunc) my_object_init -> especifica la funcin de inicializacin
 de instancias de la clase
como veis, es bastante sencillo
despues de rellenar esta struct, simplemente se llama a g_type_register_static
 con:
G_TYPE_OBJECT -> identificador de la clase base
"MyObject" -> nombre de la clase
&info -> struct recien rellenada
0 -> 0
:-)
como veis, nos faltan dos cosas, que son las dos funciones my_object*_init

:
void my_object_class_init (MyObjectClass *klass);
void my_object_init (MyObject *obj, MyObjectClass *klass);
estas dos funciones son donde haremos todo el cdigo de inicializacin de
 la clase (my_object_class_init) y de cada una de las instancias que se
 creen (my_object_init)
en _class_init normalmente se instalan los mtodos virtuales de la clase,
 etc
que luego veremos, si nos da tiempo
en _init, simplemente se inicializan los valores privados a cada una de
 las instancias
void my_object_init (MyObject *obj, MyObjectClass *klass);
'obj' es un puntero a una nueva instancia que se acaba de crear
mientras que 'klass' es un puntero a la estructura de clase, que normalmente,
 en esta funcin, no se usa
por ejemplo, si necesitasemos almacenar valores privados
en cada instancia de la clase MyObject
declarariamos MyObject como:
typedef struct {
  GObject obj;
  gpointer puntero_privado;
  gint entero_privado;
} MyObject;
en my_object_init hariamos:
obj->puntero_privado = NULL;
obj->entero_privado = 0;
(o cualquier valor de inicio que quisieramos)
hay alguna pregunta sobre esto, o paso a las seales y mtodos virtuales?

mhz
creo que esta claro
hasta el momento rodrigo.-
rodrigo
bien
como he comentado antes
podemos simular lo que se conoce en POO como mtodos virtuales
es decir
podemos declarar una clase con una serie de mtodos
y hacer distintas implementaciones de ese mtodo
en las distintas clases derivadas de dicha clase
esto se hace declarando, en la struct *Class
los distintos mtodos virtuales, en forma de punteros a funciones
por ejemplo
typedef struct {
  GObjectClass parent_class;
  void (* metodo_1) (MyObject *obj, gint param1, gboolean param2);
} MyObjectClass;
podriamos hacer una implementacin de ese mtodo en MyObject:
void my_object_metodo_1 (MyObject *obj, gint param1, gboolean param2)
{
  ...
}
void my_object_class_init (MyObjectClass *klass)
{
  klass->metodo_1 = my_object_metodo_1;
}
podriamos posteriormente crear nuevas clases basadas en MyObject
que tuvieran su propia implementacion de ese mtodo
simplemente, en su _class_init harian lo mismo
aunque de forma distinta:
typedef struct {
  MyObject obj;
} MyObjectChild;
typedef struct {
  MyObjectClass parent_class;
} MyObjectChildClass;
void my_object_child_class_init (MyObjectChildClass *klass)
{
  MyObjectClass *object_class = (MyObjectClass *) klass;
  object_class->metodo_1 = my_object_child_metodo_1;
}
y por supuesto:
void my_object_child_metodo_1 (MyObject *obj, gint param1, gboolean param2)

{ ... }
bien, ahora vamos a ver como crear instancias de las clases que hemos definido

es muy sencillo:
un segundo, por favor
mhz
paciencia, mientras esperamos a rodrigo.-
mientras llega pueden pensar con calma sus preguntas.
rodrigo
perdn
bien deca:
<rodrigo> bien, ahora vamos a ver como crear instancias de las clases
 que hemos definido
<rodrigo> es muy sencillo:
objeto = g_objecT_new (tipo_del_objeto, ...);
normalmente, esto es:
MyObject *obj;
obj = g_object_new (my_object_get_type (), NULL);
en get_type, si recordais
registrabamos la clase en GLib
(g_type_register_static)
que devuelve un GType
si mirais el codigo vereis que hay:
static GType type = 0;
<rodrigo>      if (!type) {
se usa una variable esttica, que se inicializa a 0
de forma que slo se registra (if (!type)) la clase una sola vez
ese GType identifica la clase
y g_object_new, mediante ese id
sabe cmo crear instancias de la clase
vamos a ver un ejemplo ms claro de los mtodos virtuales
vimos antes:
<rodrigo> en my_object_init hariamos:
<rodrigo> obj->puntero_privado = NULL;
<rodrigo> obj->entero_privado = 0;
imaginaros que hacemos:
obj->puntero_privado = g_malloc (2048);
es decir, asignamos memoria para esa instancia de la clase
como os podreis imaginar esta memoria, en algun momento, habr que liberarla

GObject (la clase base), incluye un mtodo virtual que es finalize:
void (* finalize) (GObject *obj)
que podemos usar fcilmente en nuestras clases:
void my_object_class_init (MyObjectClass *klass)
{
  GObjectClass *object_class = (GobjectClass *) klass;
  object_class->finalize = my_object_finalize;
}
void my_object_finalize (GObject *obj)
{
  MyObject *myobj = (MyObject *) obj;
  g_free (myobj->puntero_privado);
}
bien, creo que por hoy voy a parar para las preguntas
y la semana que viene seguimos con seales, propiedades, etc
alguna pregunta?
ah, simplemente aadir, en relacin al polimorfismo
que cada instancia de la clase MyObject puede ser tratada como un GObject

de forma que podemos hacer:
g_object_loquesea (myobj, ...);
es decir, myobj es un MyObject, pero tambien es un GObject
pues si no hay preguntas, terminamos
mhz
pregunta rodrigo.-
<pipo> que si lo ultimo no necesita casts?
rodrigo
mhz: lo necesita para compilar (GObject *)
y, si quieres comprobacin de tipos:
g_object_loquesea (GOBJECT (myobj), ...);
no lo he comentado, pero con cada clase se suministran unas macros:
#define GTK_TYPE_LABEL            (gtk_label_get_type ())
#define GTK_LABEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LABEL,
 GtkLabel))
#define GTK_LABEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_LABEL,
 GtkLabelClass))
#define GTK_IS_LABEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_LABEL))

#define GTK_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_LABEL))

#define GTK_LABEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_LABEL,
 GtkLabelClass))
que podemos usar para distintas cosas
* GTK_TYPE_LABEL -> obtener el GType de la clase
* GTK_LABEL -> hacer un casting con comprobacin de tipos en tiempo de
 ejecucin
* GTK_LABEL_CLASS -> lo mismo pero para la *Class
* GTK_IS_LABEL -> comprobacin de que realmente es del tipo GTK_TYPE_LABEL

* GTK_IS_LABEL_CLASS -> lo mismo pero para la *Class
* GTK_LABEL_GET_CLASS -> para obtener la *Class a partir de una instancia

de la misma forma, para nuestra clase MyObject
se podran suministrar esas mismas macros:
#define MY_OBJECT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_OBJECT_TYPE,
 MyObject))
...
eso en un .h
mhz
esta claro.-
rodrigo
o nadie escucha :-)
--- rodrigo sets mode -m #gnome-hispano
--- rodrigo gives channel operator status to gnomero
mhz
alguna otra duda o consulta ?
Creo que eso ha sido todo por hoy .-
muchas gracias por su asistencia.
y muchas gracias a ti rodrigo por esta charla magistral
rodrigo
:-)
telemaco
plas plas plas
garnacho
[plas]* :-)
**** ENDING LOGGING AT Mon Jan 13 20:06:08 2003