On Fri, 2007-05-04 at 10:28 -0400, Alejandro Valdes Jimenez wrote:
> Adjunto un ejemplo que hice de gobject para que alguien si tiene un
> tiempecito le de una miradita... quiero saber si está bien
> implementado
> y si es un buen ejemplo.

Le eché una mirada rápida, no soy experto en GObject, pero tengo algunos
comentarios, ojalá te sean útiles:

- Para definir el tipo te conviene usar la macro G_DEFINE_TYPE(), ya que
  evita que escribas a mano la declaración de varias funciones y
  cabeceras que son siempre iguales. Ve la documentación sobre esa macro
  y las otras relacionadas. 

- Los atributos públicos que definiste (nombre, edad) típicamente 
  serían declarados de otra manera: como datos privados, y con   
  funciones _get_name/age (), _set_name/age (). Además, si quieres
  utilizar 100% GObject, deberías instalar esas propiedades en la 
  clase. Para eso necesitarías:

  - Agregar una enumeración con todas las propiedades, para poder 
    asignarle a cada una un entero distinto:

  enum {
      PROP_NAME = 1,
      PROP_AGE
  };

  - En _class_init ():

    /* substituir las funciones set/get_property por defecto de la 
       clase padre por unas definidas en nuestra clase */

    g_object_class->set_property = my_object_set_property;
    g_object_class->get_property = my_object_get_property;

    /* ... */

    /* instalar una propiedad para guardar el nombre de la persona */ 
    g_object_class_install_property (gobject_class, 
                                     PROP_NAME,
                                     g_param_spec_string ("name", 
                                                          "Name",
                                                          "The name of the 
person",
                                                          "anónimo",
                                                          G_PARAM_READABLE | 
G_PARAM_WRITEABLE));

   /* ... */


  Esta llamada lo que hace es instalar en tu clase una propiedad de
  lectura/escritura "name" de tipo string, con valor por defecto
  "anónimo". Haces algo similar para la edad, pero usas 
  g_param_spec_int (). Haces lo mismo para cada una de las otras 
  propiedades públicas que quieras instalar.

  Todavía faltaría declarar las funciones set/get_property (). Estas se
  deben ver así aproximadamente:

  my_object_set_property (GObject *object,
                          guint    prop_id,
                          const GValue *value,
                          GParamSpec   *pspec)
  {
        MyObjectPrivate *priv = MY_OBJECT (object)->priv;

    switch (prop_id) {
        case PROP_NAME:
            /* guardar la propiedad en mi string privado */
            if (priv->name) {
                 g_free (priv->name);
            } 
            priv->name = g_strdup (g_value_get_string (value));
            break;
        case PROP_AGE:
            priv->age = g_value_get_int (value);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        }
    }

  
  my_object_get_property() es bastante similar, 

  my_object_get_property (GObject *object,
                          guint    prop_id,
                          const GValue *value,
                          GParamSpec   *pspec)
  {
        MyObjectPrivate *priv = MY_OBJECT (object)->priv;

        switch (prop_id) {
        case PROP_NAME:
          g_value_set_string (value, priv->name);
          break;
        case PROP_AGE:
          g_value_set_int (value, priv->age);
          break;
       default:
            /* warn again... */
        }
  }

- La declaración de métodos usando punteros a función que hiciste tiene 
  sentido exclusivamente si deseas que una clase heredada cambie la   
  manera de realizar la acción que dicho método haría por defecto, y 
  aún así poder ejecutarla tu mismo. Si lo que deseas es escribir un
  simple método sin nada especial, que no sea reimplementable por las 
  clases heredadas, no necesitas agregar el puntero a función.

  De todas maneras, nunca querrías que tus usuarios llamen a métodos 
  usando directamente punteros a funciones. Tener algo como 

  my_object_print (MyObject *object) 
  {
     object->print_function (object);
  }

  Sería ideal. Así para los usuarios es transparente que la función haya
  sido reemplazada o no.

- La declaración de la estructura privada en un archivo separado 
  (-private.h) sólo es útil si vas a dividir el código de tu clase 
  en más de un archivo. En caso contrario (como en tu ejemplo), bastaría
  con declararla en el mismo archivo .c

Algunas cuestiones de estilo con respecto a tus métodos:

- En vez de hacer las llamadas 

    obj = persona_main_app_new ();
    obj->agregar_persona (obj,"matias",1);

  Yo asignaría los valores de la persona directamente en el constructor:

    obj = persona_new ("matías", 1);

  O también:

    obj = persona_new ();
    persona_set_age (obj, 1);
    persona_set_name (obj, "matias");

  O, si instalaste las propiedades a la GObject, podrías incluso hacerlo
  así:

    obj = persona_new ();
    g_object_set_property (obj, "age", 1);
    g_object_set_property (obj, "name", "matias");



- El nombre (PersonaMainApp) me suena familiar :-)


Eso sería. Espero te sean útiles estos comentarios. Si quieres ver un
ejemplo completo de una clase escrita con GObject, que instala
propiedades, señales, y tiene dos constructores, te recomiendo mirar los
archivos eog/src/eog-print-preview.[ch], que pueden servirte.

¡Saludos y que te diviertas con GObject!

Claudio
-- 
Claudio Saavedra <[EMAIL PROTECTED]>

Responder a