Справочное описание GObject |
---|
Интерфейсы GType очень похожи на интерфейсы Java. Они позволяют описывать общий API которого будут придерживаться несколько классов. Представьте кнопки play, pause и stop в hifi-оборудовании - они могут рассматриваться как интерфейс воспроизведения. Как только вы поймёте что они значат, вы сможете управлять вашим cd-player, mp3-player или чем угодно использующим эти символы. Для объявления интерфейса вы должны зарегистрировать неинстанциируемый классифицируемый тип который происходит из GTypeInterface. Следующая часть кода объявляет такой интерфейс.
#define MAMAN_IBAZ_TYPE (maman_ibaz_get_type ())
#define MAMAN_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_IBAZ_TYPE, MamanIbaz))
#define MAMAN_IS_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_IBAZ_TYPE))
#define MAMAN_IBAZ_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_IBAZ_TYPE, MamanIbazInterface))
typedef struct _MamanIbaz MamanIbaz; /* dummy object */
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);
Функция интерфейса, maman_ibaz_do_action
реализуется довольно простым способом:
void maman_ibaz_do_action (MamanIbaz *self)
{
MAMAN_IBAZ_GET_INTERFACE (self)->do_action (self);
}
maman_ibaz_get_type
регистрирует тип с именем MamanIBaz
который наследует G_TYPE_INTERFACE. Все интерфейсы должны быть дочерними G_TYPE_INTERFACE в дереве иерархии.
Интерфейс определяется только одной структурой которая должна содержать структуру
GTypeInterface в качестве первого элемента.
Структура интерфейса, как ожидается, содержит указатели функции методов интерфейса. Это хороший стиль для определения
вспомогательных функций для каждого метода интерфейса который просто называют непосредственно методом интерфейса:
maman_ibaz_do_action
- один из них.
Как только интерфейсный тип зарегистрирован, вы должны зарегистрировать реализации для этих интерфейсов.
Функция с именем maman_baz_get_type
регистрируется как новый GType с именем MamanBaz
который наследует GObject и реализует
интерфейс MamanIBaz.
static void maman_baz_do_action (MamanIbaz *self)
{
g_print ("Baz implementation of IBaz interface Action.\n");
}
static void
baz_interface_init (gpointer g_iface,
gpointer iface_data)
{
MamanIbazInterface *iface = (MamanIbazInterface *)g_iface;
iface->do_action = maman_baz_do_action;
}
GType
maman_baz_get_type (void)
{
static GType type = 0;
if (type == 0) {
static const GTypeInfo info = {
sizeof (MamanBazInterface),
NULL, /* base_init */
NULL, /* base_finalize */
NULL, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (MamanBaz),
0, /* n_preallocs */
NULL /* 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_IBAZ_TYPE,
&ibaz_info);
}
return type;
}
g_type_add_interface_static
делает запись в систему типов что данный тип также реализует FooInterface
(foo_interface_get_type
возвращает тип FooInterface).
Структура GInterfaceInfo содержит информацию
о реализации интерфейса:
struct _GInterfaceInfo
{
GInterfaceInitFunc interface_init;
GInterfaceFinalizeFunc interface_finalize;
gpointer interface_data;
};
Когда инстанциируемый классифицируемый тип регистрируемый при реализации интерфейса создаётся впервые,
его структура класса инициализируется процессом описанным в
раздел “Instantiable classed types: objects”. Как только структура класса инициализирована, функция
type_class_init_Wm
(реализована в gtype.c
)
инициализирует реализацию интерфейса связанного с этим типом вызывая type_iface_vtable_init_Wm
для каждого интерфейса.
Первый буфер памяти распределяется для содержания сструктуры интерфейса. Родительская структура интерфейса копируется поверх новой сструктуры интерфейса (родительский интерфейс уже инициализирован в этой точке). Если нет родителя интерфейса, структура интерфейса инициализируется нулями. Затем инициализируются поля g_type и g_instance_type: g_type устанавливается в значение типа наследуемого интерфейса, а g_instance_type устанавливается в значение типа который реализует этот интерфейс.
Наконец, интерфейс наследует функцию base_init
и зтем вызывается функция реализации
interface_init
. Важно понять, что если есть множество реализаций интерфейса, функции
base_init
и interface_init
будут вызваны один раз для каждой
инициализируемой реализиции.
Таким образом функции base_init обычно содержат локальную статическую логическую переменную которая гарантирует что тип интерфейса инициализирован только один раз, даже если есть множество реализаций интерфейса:
static void
maman_ibaz_base_init (gpointer g_iface)
{
static gboolean initialized = FALSE;
if (!initialized) {
/* create interface signals here. */
initialized = TRUE;
}
}
Если вы находите материал об иерархии интерфейса непостежимым, вы правы: он непостежим, но я ничего не могу с этим сделать. Я могу только суммировать то что вы должны знать об интерфейсах:
Процессс описанный выше может быть резюмирован следующим:
Таблица 2. Инициализация интерфейса
Время вызова | Вызываемая функция | Параметры функции | Ремарка |
---|---|---|---|
Первый вызов g_type_create_instance для реализации типового интерфейса
|
Интерфейсная функция base_init | В интерфейсной vtable | Здесь регистрируются сигналы интерфейса (используйте локальную статическую логическую переменную как описано выше, чтобы исключить двойную регистрацию). |
Интерфейсная функция interface_init | В интерфейсной vtable | Инициализирует реализацию интерфейса. То есть, инициализирует указатели метода интерфейса в структуре интерфейса в функции реализации. |
Маловероятно (то есть: я не знаю никого кто использует это)
что вам потребуются другие более причудливые вещи которые описаны в следующем разделе
(раздел “Interface Destruction”).
Когда последний экземпляр инстанциированного типа, зарегистрированного реализацией интерфейса, уничтожен,
реализация интерфейса связанная с типом тоже уничтожается с помощью
type_iface_vtable_finalize_Wm
(в gtype.c
).
type_iface_vtable_finalize_Wm
вызывает сначала функцию реализации
interface_finalize
, а зтем функцию наивысшего наследуемого интерфейса
base_finalize
.
Снова, важно понять, как в
разделе “Interface Initialization”,
и interface_finalize
и base_finalize
вызываются только один раз для уничтожения каждой реализации интерфейса. Таким образом,
если вы используете одну из этих функций, вы должны использовать статическую целочисленную переменную
которая должна считать количество экземпляров реализации интерфейса чтобы класс интерфейса был уничтожен
только один раз (когда целочисленная переменная достигает нуля).
Выше описанный процесс можно резюмировать следующим:
Таблица 3. Уничтожение интерфейса
Время вызова | Вызываемая функция | Параметры функции |
---|---|---|
Последний вызов g_type_free_instance для типовой реализации интерфейса
|
интерфейсная функция interface_finalize | В интерфейсной vtable |
интерфейсная функция base_finalize | В интерфейсной vtable |
Теперь когда вы прочли этот раздел, вы можете забыть про него. Пожалуйста, забудьте так быстро как только возможно.