The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа

Как определить и реализовать новый GObject?

Шаблонный заголовочный код
Шаблонный код
Конструирование объекта
Уничтожение объекта
Объектные методы
Невиртуальные общие методы
Виртуальные общие методы
Виртуальные закрытые методы
Формирование цепочки

Конечно, в основном люди задаются вопросом: как реализовать подкласс GObject. Иногда потому что нужно создать собственную иерархию класса, иногда потому что нужен подкласс одного из виджетов GTK+. Этот раздел сфокусирован над реализацией подтипов GObject. Пример исходного кода связанного с этим разделом может быть найден в исходниках документации, в каталоге sample/gobject:

Шаблонный код заголовка

Первый шаг написания кода для вашего 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 самостоятельно заголовочные файлы в которых нуждается перед включением вашего заголовочного файла. Это ускоряет компиляцию потому что минимизирует работу препроцессора. Это может быть использовано вместе с повторной декларацией определённых неиспользуемых типов в клиентском коде для минимизации времени компилирования зависимостей и таким образом укорить компиляцию.




Спонсоры:
MIRhosting
Fornex
Hosting by Ihor
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2019 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру