| |
Справочное описание GObject |
---|
Конечно, в основном люди задаются вопросом: как реализовать подкласс GObject.
Иногда потому что нужно создать собственную иерархию класса, иногда потому что нужен подкласс одного из виджетов GTK+.
Этот раздел сфокусирован над реализацией подтипов GObject. Пример исходного кода связанного с этим разделом может быть найден
в исходниках документации, в каталоге sample/gobject
:
maman-bar.{h|c}
: это исходник для объекта который наследует
GObject и который показывает как
декларируются разные типовые методы объекта.
maman-subbar.{h|c}
: это исходник для объекта который наследует
MamanBar и который показывает как переписать некоторые его родительские методы.
maman-foo.{h|c}
: это исходник для объекта который наследует
GObject и который декларирует сигнал.
test.c
: это основной исходник который инстанциирует экземпляр типа и осуществляет API.
Первый шаг написания кода для вашего GObject - написать типовой заголовок который содержит необходимые декларации типа, функций и макросов. Каждый из этих элементов является соглашением которому следует не только код GTK+, но также большинство пользователей GObject. Подумайте дважды прежде чем нарушать правила заявленные ниже:
Если пользователи немного привыкли к коду GTK+ или любому коду Glib, они будут удивлены вашими нарушениями и привыкание к тому что вы нарушили соглашения займёт время (деньги) и будет их раздражать (это нехорошо)
Вы должны понимать что возможно эти соглашения разрабатывали достаточно умные и опытные люди: возможно они были по крайней мере частично правы. Попробуйте преодолеть своё эго.
Выберете согласованное имя для ваших файлов заголовков и исходного кода и придерживайтесь его:
использовать тире для отделения префикса от типового имени: maman-bar.h
и
maman-bar.c
(это соглашение используется Nautilus и большинством библиотек GNOME).
использовать символ подчеркивания для разделения префикса и типового имени: maman_bar.h
и
maman_bar.c
.
Не отделять префикс от типового имени: mamanbar.h
и
mamanbar.c
. (это соглашение используется в GTK+)
Я лично предпочитаю первое соглашение: оно позволяет легче читать имена файлов людям со слабым зрением, таким как я.
Когда вам необходимы некоторые закрытые (внутренние) декларации в нескольких (под)классах,
вы можете определить их в закрытом заголовочном файле который часто называют добавляя ключевое слово
private к общему имени заголовочных файлов.
Например, можно было бы использовать maman-bar-private.h
,
maman_bar_private.h
или mamanbarprivate.h
.
Обычно такие закрытые заголовочные файлы не устанавливаются.
Основные соглашения для любых заголовков GType описаны в “Conventions”. Большинство кода основанного на GObject также придерживается следующих соглашений: выберете одно из них и придерживайтесь его.
Если вы хотите задекларировать тип с именем bar и префиксом maman, имя типового экземпляра
MamanBar
и его класс MamanBarClass
(имена регистрозависимы). Общепринято декларировать это кодом подобно следующему:
/* * Информация о правах и лицензионных соглашениях. */ #ifndef MAMAN_BAR_H #define MAMAN_BAR_H /* * Потенциально, включите другие заголовочные файлы от которого зависит этот. */ /* * Напечатайте макроопределения. */ typedef struct _MamanBar MamanBar; typedef struct _MamanBarClass MamanBarClass; struct _MamanBar { GObject parent; /* члены экземпляра */ }; struct _MamanBarClass { GObjectClass parent; /* члены класса */ }; /* используемый MAMAN_BAR_TYPE */ GType maman_bar_get_type (void); /* * Определения метода. */ #endif
Большинство GTK+ типов декларируют свои закрытые поля в общих заголовках с комментарием /* private */, рассчитывая на благоразумие пользователей не пытаться играть с этими полями. Поля не отмеченные как закрытые рассматриваются как общие по умолчанию. Комментарий /* protected */ (с семантикой C++) также используются, главным образом в библиотеке GType, в коде написанном Tim Janik.
struct _MamanBar { GObject parent; /*< private >*/ int hsize; };
Весь код Nautilus и большинство библиотек GNOME используют закрытые косвенные члены, как описано Herb Sutter в его статьях Pimpl (смотрите Compilation Firewalls и The Fast Pimpl Idiom : он суммировал разные проблемы лучше меня).
typedef struct _MamanBarPrivate MamanBarPrivate; struct _MamanBar { GObject parent; /*< private >*/ MamanBarPrivate *priv; };
Не используйте имя private
, так как это зарегистрированное ключевое слово c++.
Закрытая структура определяемая в .c файле, инстанциируется в объектной функции
init
а уничтожается в объектной функции finalize
.
static void maman_bar_finalize (GObject *object) { MamanBar *self = MAMAN_BAR (object); /* do stuff */ g_free (self->priv); } static void maman_bar_init (GTypeInstance *instance, gpointer g_class) { MamanBar *self = MAMAN_BAR (instance); self->priv = g_new0 (MamanBarPrivate,1); /* do stuff */ }
Подобная альтернативная возможность, начиная с версии Glib 2.4, определить закрытую структуру в .c файле,
декларируя её как закрытую структуру в maman_bar_class_init
используя
g_type_class_add_private
.
Вместо распределения памяти в maman_bar_init
указать закрытую область памяти сохранённую в
экземпляре позволяя удобный доступ к этой структуре.
Таким образом закрытая структура будет прикреплена к каждому вновь созданному объекту системы GObject.
Вам не нужно освобождать или распределять закрытую структуру, только объекты или указатели которые она может содержать.
Другое преимущество этой версии перед предыдущей в том что это уменьшает фрагментацию памяти, так как общие и закрытые
части памяти экземпляра распределяются одновременно.
typedef struct _MamanBarPrivate MamanBarPrivate; struct _MamanBarPrivate { int private_field; }; static void maman_bar_class_init (MamanBarClass *klass) { ... g_type_class_add_private (klass, sizeof (MamanBarPrivate)); ... } static void maman_bar_init (GTypeInstance *instance, gpointer g_class) { MamanBar *self = MAMAN_BAR (instance); self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MAMAN_BAR_TYPE, MamanBarPrivate); /* do stuff */ }
Наконец, есть различные соглашения включения заголовков. Снова, выберете одно и придерживайтесь ему. Я лично использую любое из двух без разницы, в зависимости от кодовой базы в которой работаю: согласованное правило.
Некоторые люди добавляют заголовочный файл с множеством директив #include для перемещения по всем заголовкам необходимым для компиляции кода клиента. Это позволяет коду клиента просто включить #include "maman-bar.h".
Другие не делают никаких #include и ожидают что клиент включит #include самостоятельно заголовочные файлы в которых нуждается перед включением вашего заголовочного файла. Это ускоряет компиляцию потому что минимизирует работу препроцессора. Это может быть использовано вместе с повторной декларацией определённых неиспользуемых типов в клиентском коде для минимизации времени компилирования зависимостей и таким образом укорить компиляцию.
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |