GLib
  • Initial page
  • I. Концепция
    • Background
    • Типы данных и программирование
  • Динамическая система типов Glib
    • Введение
    • Функции копирования
    • Соглашения
    • Неинстанциируемые не классифицированные базовые типы
    • Инстанциируемые классифицируемые типы: объекты
    • Неинстанциированные классифицированные типы: Интерфейсы.
  • Основной класс GObject
    • Введение
    • Инстанциация объекта
    • Объектное управление памятью
    • Свойства объекта
  • Система сообщений GObject
    • Замыкания
    • Сигналы
  • II. Описание API
    • GType
    • GTypePlugin
    • GTypeModule
    • GObject часть 1
    • GObject часть 2
    • Enums and Flags
    • GBoxed
    • Generic Values
    • Parameters and Values часть 1
    • Parameters and Values часть 2
    • Parameters and Values часть 3, ага
    • Value arrays
  • III. Описание инструментов
    • glib-mkenums
    • glib-genmarshal
    • gobject-query
  • IV. Руководство
    • Как определить и реализовать новый GObject?
    • Объектные методы
    • Как определять и реализовывать Интерфейсы?
    • Как создавать и использовать сигналы
    • Как пользователи могут злоупотреблять сигналами (и почему некоторые думают что это хорошо)
  • V. Об инструментах
    • Об инструментах
  • GTK 4
    • GtkApplication и GtkApplicationWindow
    • Widgets
Powered by GitBook
On this page
  • Как определять Интерфейсы?
  • Как определить реализацию Интерфейса?
  • Предпосылки определения Интерфейса

Was this helpful?

  1. IV. Руководство

Как определять и реализовывать Интерфейсы?

PreviousОбъектные методыNextКак создавать и использовать сигналы

Last updated 6 years ago

Was this helpful?

Как определять Интерфейсы?

Большая часть определения интерфейса уже показана но я чувствую что нужно показать как конкретно создаётся интерфейс. Исходные коды примеров связанных с этим разделом можно найти в исходном пакете документации, в файле sample/interface/maman-ibaz.{h|c}.

Как и раньше, сначала получаем правильный заголовок:

#ifndef MAMAN_IBAZ_H
#define MAMAN_IBAZ_H

#include <glib-object.h>

#define MAMAN_TYPE_IBAZ                (maman_ibaz_get_type ())
#define MAMAN_IBAZ(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_IBAZ, MamanIbaz))
#define MAMAN_IS_IBAZ(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_IBAZ))
#define MAMAN_IBAZ_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_TYPE_IBAZ, MamanIbazInterface))


typedef struct _MamanIbaz MamanIbaz; /* макет объекта */
typedef struct _MamanIbazInterface MamanIbazInterface;

struct _MamanIbazInterface {
  GTypeInterface parent;

  void (*do_action) (MamanIbaz *self);
};

GType maman_ibaz_get_type (void);

void maman_ibaz_do_action (MamanIbaz *self);

#endif /*MAMAN_IBAZ_H*/
  • _GET_CLASS макрос называется _GET_INTERFACE и реализуется не с помощью G_TYPE_INSTANCE_GET_CLASS, а с помощью G_TYPE_INSTANCE_GET_INTERFACE.

  • Тип экземпляра MamanIbaz определён не полностью: он используется просто как абстрактный тип который представляет экземпляр любого объекта реализующего интерфейс.

Реализация самого типа MamanIbaz тривиальна:

  • maman_ibaz_get_type регистрирует тип в системе типов.

  • maman_ibaz_do_action разыменовывает структуру класса для доступа к связанным с ней функциям класса и их вызова.

static void
maman_ibaz_base_init (gpointer g_class)
{
  static gboolean initialized = FALSE;

  if (!initialized) {
    /* здесь создаём сигналы интерфейса. */
    initialized = TRUE;
  }
}

GType
maman_ibaz_get_type (void)
{
  static GType type = 0;
  if (type == 0) {
    static const GTypeInfo info = {
      sizeof (MamanIbazInterface),
      maman_ibaz_base_init,   /* base_init */
      NULL,   /* base_finalize */
      NULL,   /* class_init */
      NULL,   /* class_finalize */
      NULL,   /* class_data */
      0,
      0,      /* n_preallocs */
      NULL    /* instance_init */
    };
    type = g_type_register_static (G_TYPE_INTERFACE, "MamanIbaz", &info, 0);
  }
  return type;
}

void maman_ibaz_do_action (MamanIbaz *self)
{
  MAMAN_IBAZ_GET_INTERFACE (self)->do_action (self);
}

Как определить реализацию Интерфейса?

Как только интерфейс определён, его реализация довольно тривиальна. Исходный код, показывающий как это делается для определённого интерфейса IBaz в предыдущем разделе, расположен в sample/interface/maman-baz.{h|c}.

Первый шаг - определяем обычную GType. Здесь мы решили использовать GType которая наследует GObject. Её имя MamanBaz:

#ifndef MAMAN_BAZ_H
#define MAMAN_BAZ_H

#include <glib-object.h>

#define MAMAN_TYPE_BAZ             (maman_baz_get_type ())
#define MAMAN_BAZ(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAZ, Mamanbaz))
#define MAMAN_BAZ_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MAMAN_TYPE_BAZ, MamanbazClass))
#define MAMAN_IS_BAZ(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAZ))
#define MAMAN_IS_BAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MAMAN_TYPE_BAZ))
#define MAMAN_BAZ_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MAMAN_TYPE_BAZ, MamanbazClass))


typedef struct _MamanBaz MamanBaz;
typedef struct _MamanBazClass MamanBazClass;

struct _MamanBaz {
  GObject parent;
  int instance_member;
};

struct _MamanBazClass {
  GObjectClass parent;
};

GType maman_baz_get_type (void);


#endif //MAMAN_BAZ_H

В этом заголовке нет ничего сверхъестественного или страшного: он не определяет никакого особенного API и не наследует особенный тип.

Второй шаг - реализация maman_baz_get_type:

GType 
maman_baz_get_type (void)
{
  static GType type = 0;
  if (type == 0) {
    static const GTypeInfo info = {
      sizeof (MamanBazClass),
      NULL,   /* base_init */
      NULL,   /* base_finalize */
      NULL,   /* class_init */
      NULL,   /* class_finalize */
      NULL,   /* class_data */
      sizeof (MamanBaz),
      0,      /* n_preallocs */
      baz_instance_init    /* instance_init */
    };
    static const GInterfaceInfo ibaz_info = {
      (GInterfaceInitFunc) baz_interface_init,    /* interface_init */
      NULL,               /* interface_finalize */
      NULL                /* interface_data */
    };
    type = g_type_register_static (G_TYPE_OBJECT,
                                   "MamanBazType",
                                   &info, 0);
    g_type_add_interface_static (type,
                                 MAMAN_TYPE_IBAZ,
                                 &ibaz_info);
  }
  return type;
}

baz_interface_init, функция инициализации интерфейса, она тоже очень проста:

static void baz_do_action (MamanBaz *self)
{
  g_print ("Baz implementation of IBaz interface Action: 0x%x.\n", self->instance_member);
}
static void
baz_interface_init (gpointer   g_iface,
                    gpointer   iface_data)
{
  MamanIbazInteface *iface = (MamanIbazInteface *)g_iface;
  iface->do_action = (void (*) (MamanIbaz *self))baz_do_action;
}
static void
baz_instance_init (GTypeInstance   *instance,
                   gpointer         g_class)
{
  MamanBaz *self = MAMAN_BAZ(instance);
  self->instance_member = 0xdeadbeaf;
}

baz_interface_init просто инициализирует интерфейсные методы для реализации определённого MamanBaz: maman_baz_do_action не делает ничего полезного но могла бы :)

Предпосылки определения Интерфейса

Для определения что интерфейс требует присутствия других интерфейсов при реализации, GObject вводит понятие предпосылки: возможность связать список необходимых как условие интерфейсов с интерфейсом. Например, если объект A хочет реализовать интерфейс I1, и если интерфейс I1 имеет предпосылку на интерфейс I2, A реализует оба интерфейса I1 и I2.

Механизм описанный выше на практике очень похож на Java, интерфейс I1 дополняется интерфейсом I2. Пример ниже показывает GObject эквивалент:

  type = g_type_register_static (G_TYPE_INTERFACE, "MamanIbar", &info, 0);
  /* Создаём интерфейс MamanIbar требующий интерфейс MamanIbaz. */
  g_type_interface_add_prerequisite (type, MAMAN_TYPE_IBAZ);

Код показанный выше добавляет интерфейс MamanIbaz в список предпосылок MamanIbar в то время как код ниже показывает как реализовать оба интерфейса и зарегистрировать их реализацию:

static void ibar_do_another_action (MamanBar *self)
{
  g_print ("Bar implementation of IBar interface Another Action: 0x%x.\n", self->instance_member);
}

static void
ibar_interface_init (gpointer   g_iface,
                     gpointer   iface_data)
{
  MamanIbarInterface *iface = (MamanIbarInterface *)g_iface;
  iface->do_another_action = (void (*) (MamanIbar *self))ibar_do_another_action;
}


static void ibaz_do_action (MamanBar *self)
{
  g_print ("Bar implementation of IBaz interface Action: 0x%x.\n", self->instance_member);
}

static void
ibaz_interface_init (gpointer   g_iface,
                    gpointer   iface_data)
{
  MamanIbazInterface *iface = (MamanIbazInterface *)g_iface;
  iface->do_action = (void (*) (MamanIbaz *self))ibaz_do_action;
}

static void
bar_instance_init (GTypeInstance   *instance,
                   gpointer         g_class)
{
  MamanBar *self = (MamanBar *)instance;
  self->instance_member = 0x666;
}

GType 
maman_bar_get_type (void)
{
  static GType type = 0;
  if (type == 0) {
    static const GTypeInfo info = {
      sizeof (MamanBarClass),
      NULL,   /* base_init */
      NULL,   /* base_finalize */
      NULL,   /* class_init */
      NULL,   /* class_finalize */
      NULL,   /* class_data */
      sizeof (MamanBar),
      0,      /* n_preallocs */
      bar_instance_init    /* instance_init */
    };
    static const GInterfaceInfo ibar_info = {
      (GInterfaceInitFunc) ibar_interface_init,   /* interface_init */
      NULL,                                       /* interface_finalize */
      NULL                                        /* interface_data */
    };
    static const GInterfaceInfo ibaz_info = {
      (GInterfaceInitFunc) ibaz_interface_init,   /* interface_init */
      NULL,                                       /* interface_finalize */
      NULL                                        /* interface_data */
    };
    type = g_type_register_static (G_TYPE_OBJECT,
                                   "MamanBarType",
                                   &info, 0);
    g_type_add_interface_static (type,
                                 MAMAN_TYPE_IBAZ,
                                 &ibaz_info);
    g_type_add_interface_static (type,
                                 MAMAN_TYPE_IBAR,
                                 &ibar_info);
  }
  return type;
}

Полный исходный код, показывающий как определить интерфейс MamanIbar который требует MamanIbaz и как реализовать интерфейс MamanIbar, расположен в sample/interface/maman-ibar.{h|c} и sample/interface/maman-bar.{h|c}.

Свойства интерфейса

static void
maman_ibaz_base_init (gpointer g_iface)
{
  static gboolean initialized = FALSE;

  if (!initialized) {
    /* здесь создаём сигналы интерфейса. */

    g_object_interface_install_property (g_iface,
                g_param_spec_string ("name",
                    "maman_ibaz_name",
                    "Name of the MamanIbaz",
                    "maman",
                    G_PARAM_READWRITE));
    initialized = TRUE;
  }
}

Одно замечание - задекларированному свойству не был присвоен целочисленный ID. Причина в том, что целочисленный ID этого свойства используется только внутри методов получения и установки, а так как интерфейс не реализует свойства, нет необходимости привязывать целочисленный ID к свойствам интерфейса.


struct _MamanBaz {
  GObject parent;
  gint instance_member;
  gchar *name;        /* метка для свойства */
};

enum
{
  ARG_0,
  ARG_NAME
};

GType 
maman_baz_get_type (void)
{
  static GType type = 0;
  if (type == 0) {
    static const GTypeInfo info = {
      sizeof (MamanBazClass),
      NULL,   /* base_init */
      NULL,   /* base_finalize */
      baz_class_init, /* class_init */
      NULL,   /* class_finalize */
      NULL,   /* class_data */
      sizeof (MamanBaz),
      0,      /* n_preallocs */
      baz_instance_init    /* instance_init */
    };
    static const GInterfaceInfo ibaz_info = {
      (GInterfaceInitFunc) baz_interface_init,    /* interface_init */
      NULL,               /* interface_finalize */
      NULL                /* interface_data */
    };
    type = g_type_register_static (G_TYPE_OBJECT,
                                   "MamanBazType",
                                   &info, 0);
    g_type_add_interface_static (type,
                                 MAMAN_TYPE_IBAZ,
                                 &ibaz_info);
  }
  return type;
}

static void
maman_baz_class_init (MamanBazClass * klass)
{
  GObjectClass *gobject_class;

  gobject_class = (GObjectClass *) klass;

  parent_class = g_type_class_ref (G_TYPE_OBJECT);

  gobject_class->set_property = maman_baz_set_property;
  gobject_class->get_property = maman_baz_get_property;

  g_object_class_override_property (gobject_class, ARG_NAME, "name");
}

static void
maman_baz_set_property (GObject * object, guint prop_id,
                        const GValue * value, GParamSpec * pspec)
{
  MamanBaz *baz;
  GObject *obj;

  /* it's not null if we got it, but it might not be ours */
  g_return_if_fail (G_IS_MAMAN_BAZ (object));

  baz = MAMAN_BAZ (object);

  switch (prop_id) {
    case ARG_NAME:
      baz->name = g_value_get_string (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
maman_baz_get_property (GObject * object, guint prop_id,
                        GValue * value, GParamSpec * pspec)
{
  MamanBaz *baz;

  /* it's not null if we got it, but it might not be ours */
  g_return_if_fail (G_IS_TEXT_PLUGIN (object));

  baz = MAMAN_BAZ (object);

  switch (prop_id) {
    case ARG_NAME:
      g_value_set_string (value, baz->name);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Этот код похож на обычную которая наследует за исключением некоторых деталей:

maman_ibaz_base_init как ожидается регистрирует интерфейсные сигналы если они есть (позже мы рассмотрим как их использовать). Убедитесь что используете статичную локальную логическую переменную чтобы не выполнять код инициализации дважды (как описано в , base_init выполняется один раз для каждой реализованной инстанциации интерфейса)

Эта функция очень похожа на подобные функции рассматриваемые нами ранее. Единственный интерфейсно-специфичный код представленный здесь это вызов который используется для информирования системы типов что зарегистрированный тип также реализует интерфейс MAMAN_TYPE_IBAZ.

Важно обратить внимание что порядок, в котором реализованные интерфейсы добавляются к основному объекту, не случаен: должна быть вызвана сначала для интерфейсов которые не имеют никаких предпосылок а зтем для остальных.

Начиная с версии glib 2.4, GObject интерфейсы могут также иметь свойства. Декларация свойств интерфейса похожа на декларацию свойств обычных типов GObject как показано в, исключая то, что используется для декларации свойств вместо .

Для включения свойства с именем 'name' типа string в код выше приведённого примера интерфейса maman_ibaz, нам нужно добавить только одну[] строчку в maman_ibaz_base_init[] как показано ниже:

История для конструкторов интерфейса также весьма тривиальна. Конструктор должен задекларировать и определить свойства обычным способом как описано в , за исключением одной мелочи: он должен задекларировать свойства интерфейса используя вместо. Следующий код демонстрирует изменения необходимые в декларации и реализации MamanBaz показанной выше:

[] Это действительно одна строка расширенная на шесть для ясности

[] может также быть вызвана из class_init но её нельзя вызвать после этой точки.

“Non-instantiable classed types: Interfaces.”
GType
GObject
“Interface Initialization”
g_type_add_interface_static
GType
g_type_add_interface_static
“Object properties”
g_object_interface_install_property
g_object_class_install_property
14
15
the section called “Object properties”
g_object_class_override_property
g_object_class_install_property
14
15
g_object_interface_install_property