Неинстанциированные классифицированные типы: Интерфейсы.

Интерфейсы 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_Wmgtype.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


Теперь когда вы прочли этот раздел, вы можете забыть про него. Пожалуйста, забудьте так быстро как только возможно.