В одной из программ на работе используется ряд самописных Gtk-виджетов для настройки фильтров изображения. Виджеты лежат в Box'е, добавляются, удаляются и настраиваются пользователем. У каждого типа виджетов свой набор настроек. Все настройки виджеты хранят в своих свойствах.

Чтобы сохранять после перезапуска программы список фильтров и их параметры, была идея сделать некий обобщенны код, не зависимый от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам.

В качестве "proof of concept" был написан код, клонирующий объект Gobject. Функция g_object_clone возвращает копию объекта, переданного ей в качестве параметра src. Код основан на примере со StackOverflow. Обращаю внимание, что полноценно копируются только простые свойства, для boxed-типов копируется указатель с инкрементированием счетчика ссылок.

Код gobject_clone.c:
// gcc `pkg-config --libs --cflags glib-2.0 gobject-2.0` gobject_clone.c point.c -o gobject_clone
#include <glib-object.h>
#include "point.h"
#include <stdio.h>
#include <string.h> //< For strcmp.

// See http://stackoverflow.com/questions/3003655/is-there-a-good-way-to-copy-a-gtk-widget?answertab=votes#tab-top
static GObject *g_object_clone(GObject *src)
{
  GObject *dst;
  GParameter *params;
  GParamSpec **specs;
  guint n, n_specs, n_params;

  specs = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), &n_specs);
  params = g_new0(GParameter, n_specs);
  n_params = 0;

  for (n = 0; n < n_specs; ++n)
    if((specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
    {
      params[n_params].name = g_intern_string(specs[n]->name);
      g_value_init(&params[n_params].value, specs[n]->value_type);
      g_object_get_property(src, specs[n]->name, &params[n_params].value);
      ++n_params;
    }

  dst = g_object_newv(G_TYPE_FROM_INSTANCE(src), n_params, params);
  g_free(specs);
  g_free(params);

  return dst;
}

// Print, clear, print again.
static void psp (Point *p)
{
  g_print("OBJECT %p\n", p);
  g_print("  Before clearing:\n");
  point_print(p);
  g_signal_emit_by_name(p, "clear");
  g_print("  After clearing:\n");
  point_print(p);
  g_print("  ...\n");
}

int main(int argc, char **argv)
{
  g_type_init();

  GArray *array = g_array_new(FALSE, FALSE, sizeof(gint));
  gint val1 = 33, val2 = 44;
  g_array_append_val(array, val1);
  g_array_append_val(array, val2);
  g_array_append_val(array, val1);

  GObject *p1 = g_object_new(TYPE_POINT, "x", 5, "y", 10, "a", array, NULL);
  GObject *p2 = g_object_clone(p1); //< Note: garray uses ref/unref for copy/free.

  psp(POINT(p1));

  g_print("Appending value '%d' to array.\n", val2);
  g_print("  ...\n");
  g_array_append_val(array, val2);

  psp(POINT(p2));

  g_clear_object(&p1);
  g_clear_object(&p2);

  return 0;
}
В качестве объектов в примере взят простенький класс Point с гитхаба за авторством Jacob Gelbman. Код point.h:
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance
#ifndef __POINT_H__
#define __POINT_H__
#include <glib.h>
#include <glib-object.h>

#define TYPE_POINT           (point_get_type             ())
#define POINT(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_POINT, Point))
#define POINT_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST    ((cls), TYPE_POINT, PointClass))
#define IS_POINT(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_POINT))
#define IS_POINT_CLASS(cls)  (G_TYPE_CHECK_CLASS_TYPE    ((cls), TYPE_POINT))
#define POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS  ((obj), TYPE_POINT, PointClass))

typedef struct _PointClass PointClass;
typedef struct _Point      Point;

struct _PointClass
{
  GObjectClass parent;
  void (*clear) (Point *self);
};

struct _Point
{
  GObject parent;
  gint    x;
  gint    y;
  GArray *array;
};

GType point_get_type(void);
void  point_print(Point *self);

#endif /*__POINT_H__*/

Код point.c:
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance
#include <glib-object.h>
#include "point.h"

G_DEFINE_TYPE(Point, point, G_TYPE_OBJECT);

enum
{
  PROP_0,
  PROP_ARRAY,
  PROP_X,
  PROP_Y
};

enum
{
  CLEAR,
  LAST_SIGNAL
};

static guint point_signals[LAST_SIGNAL] = {0};


void point_print (Point *self)
{
  g_print("    x: %d, y: %d\n", self->x, self->y);

  g_print("    ar:");
    if(self->array)
    {
      guint i;
      for(i = 0; i < self->array->len; i++)
        g_print(" %d", g_array_index(self->array, gint, i));
    }
    else
      g_print("null");
  g_print("\n");
}

static void point_clear (Point *self)
{
  g_object_set(self, "x", 0, "y", 0, "a", NULL, NULL);
}

static void point_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec)
{
  Point *point = POINT(obj);

  switch (prop_id)
  {
    case PROP_ARRAY:
      g_value_set_boxed(value, point->array);
    break;

    case PROP_X:
      g_value_set_int(value, point->x);
    break;

    case PROP_Y:
      g_value_set_int(value, point->y);
    break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
    break;
  }
}

static void point_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  Point *point = POINT(obj);

  switch (prop_id)
  {
    case PROP_ARRAY:
      if(point->array) g_array_unref(point->array);
      point->array = g_value_dup_boxed(value);
    break;

    case PROP_X:
      point->x = g_value_get_int(value);
    break;

    case PROP_Y:
      point->y = g_value_get_int(value);
    break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
    break;
  }
}

static void point_finalize(GObject *point)
{
  if(POINT(point)->array) g_array_unref(POINT(point)->array);

  G_OBJECT_CLASS(point_parent_class)->finalize(point);
}

static void point_class_init (PointClass *cls)
{
  GObjectClass *g_object_class = G_OBJECT_CLASS(cls);
  GParamSpec *array_param;
  GParamSpec *x_param;
  GParamSpec *y_param;

  g_object_class->get_property = point_get_property;
  g_object_class->set_property = point_set_property;
  g_object_class->finalize = point_finalize;

  cls->clear = point_clear;

  array_param = g_param_spec_boxed(
    "a", "a", "some int array",
    G_TYPE_ARRAY,
    G_PARAM_READWRITE);

  x_param = g_param_spec_int(
    "x", "x", "x loc of point",
    INT_MIN, /* => */ INT_MAX,
    0,
    G_PARAM_READWRITE);

  y_param = g_param_spec_int(
    "y", "y", "y loc of point",
    INT_MIN, /* => */ INT_MAX,
    0,
    G_PARAM_READWRITE);

  g_object_class_install_property(
    g_object_class,
    PROP_ARRAY,
    array_param);

  g_object_class_install_property(
    g_object_class,
    PROP_X,
    x_param);

  g_object_class_install_property(
    g_object_class,
    PROP_Y,
    y_param);

  point_signals[CLEAR] = g_signal_new(
    "clear",                               /* signal_name */
    TYPE_POINT,                            /* itype */
    G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, /* signal_flags */
    G_STRUCT_OFFSET(PointClass, clear),    /* class_offset */
    NULL,                                  /* accumulator */
    NULL,                                  /* accu_data */
    g_cclosure_marshal_VOID__VOID,         /* c_marshaller */
    G_TYPE_NONE,                           /* return_type */
    0);                                    /* n_params */
}

static void point_init(Point *point)
{
  point->array = NULL;
}


 

Copyright © 2007 DamnSmallBlog. Content is licensed under Creative Commons Attribution-Noncommercial.

Design: GeckoandFly and Blogcrowds.