Введение

Библиотека свободного программного обеспечения

Система объектов GLib, или GObject, — это библиотека свободного программного обеспечения, предоставляющая переносимую объектную систему и прозрачное взаимодействие между языками программирования. GObject предназначен как для непосредственного использования в программах на C, обеспечивая объектно-ориентированные API на основе C, так и для использования через привязки к другим языкам, чтобы обеспечить прозрачное взаимодействие между ними, например, PyGObject.

История

GObject, основанный только на GLib и libc, является краеугольным камнем GNOME и используется во всех библиотеках GTK, Pango, ATK и большинстве библиотек GNOME более высокого уровня, таких как GStreamer и приложения. До выпуска GTK+ 2.0 код, аналогичный GObject, был частью кодовой базы GTK. (Имя “GObject” ещё не использовалось — общий базовый класс назывался GtkObject.) С выпуском GTK+ 2.0 объектная система была выделена в отдельную библиотеку благодаря своей универсальности. В ходе этого процесса большинство частей класса GtkObject, не относящихся к графическому интерфейсу, были перенесены в GObject, новый общий базовый класс. Существуя как отдельная библиотека с 11 марта 2002 года (дата выпуска GTK+ 2.0), библиотека GObject теперь используется многими программами, не имеющими графического интерфейса, такими как приложения командной строки и серверные приложения.

Отношение к GLib

Хотя GObject имеет собственный набор документации и обычно компилируется в отдельную общую библиотеку, исходный код GObject находится в дереве исходного кода GLib и распространяется вместе с GLib. Поэтому GObject использует номера версий GLib и обычно поставляется в комплекте с GLib (например, Debian включает GObject в свою группу пакетов libglib2.0).

Система типов

На самом базовом уровне фреймворка GObject лежит универсальная и динамическая система типов, называемая GType. Система GType хранит описание всех объектов во время выполнения, что позволяет вспомогательному коду обеспечивать взаимодействие с привязками к различным языкам программирования. Система типов может обрабатывать любую структуру классов с одиночным наследованием, а также типы, не являющиеся классами, такие как непрозрачные указатели, строки, целые числа и числа с плавающей точкой различных размеров. Система типов знает, как копировать, присваивать и уничтожать значения, принадлежащие любому из зарегистрированных типов. Для типов, таких как целые числа, это тривиально, но многие сложные объекты используют подсчет ссылок, в то время как некоторые являются сложными, но не используют подсчет ссылок. Когда система типов "копирует" объект с подсчетом ссылок, она обычно просто увеличивает счетчик ссылок, а при копировании сложного объекта без подсчета ссылок (например, строки) обычно создается фактическая копия путем выделения памяти. Эта базовая функциональность используется для реализации GValue – типа универсального контейнера, который может хранить значения любого типа, известного системе типов. Такие контейнеры особенно полезны при взаимодействии с динамически типизированными средами, в которых все собственные значения хранятся в контейнерах с указанием типа.

Система передачи сообщений

Система обмена сообщениями GObject состоит из двух взаимодополняющих частей: замыкания и сигналы. Замыкание GObject — это обобщённая версия функции обратного вызова. Поддерживаются замыкания, написанные на C и C++, а также на любых других языках (при наличии соответствующих привязок). Это позволяет вызывать код, написанный, например, на Python и Java, через замыкание GObject. Сигналы — основной механизм, посредством которого вызываются замыкания. Объекты регистрируют прослушиватели сигналов в системе типов, определяя соответствие между конкретным сигналом и конкретным замыканием. При излучении зарегистрированного сигнала соответствующее замыкание вызывается. В GTK все собственные события графического интерфейса (например, движение мыши и действия с клавиатуры) могут генерировать сигналы GObject, на которые прослушиватели могут реагировать.

Реализация класса

Каждый класс GObject реализуется как минимум двумя структурами: структурой класса и структурой экземпляра. Структура класса соответствует vtable класса C++. Она должна начинаться со структуры класса суперкласса. Далее она содержит набор указателей на функции – по одному для каждого виртуального метода класса. Переменные, специфичные для класса, могут использоваться для эмуляции членов класса. Структура экземпляра Структура экземпляра, которая существует в одной копии на каждый экземпляр объекта, должна начинаться со структуры экземпляра суперкласса (это гарантирует, что все экземпляры начинаются с указателя на структуру класса, поскольку все фундаментальные типы, которые можно инстанцировать, обладают этим свойством). После данных, принадлежащих суперклассу, структура может содержать любые переменные, специфичные для экземпляра, соответствующие переменным-членам C++. Определение класса в системе GObject – сложная задача, требующая большого количества шаблонного кода, такого как ручное определение макросов приведения типов и запутанные заклинания регистрации типов. Кроме того, поскольку структура C не может иметь модификаторы доступа, такие как “public”, “protected” или “private”, для обеспечения инкапсуляции необходимо использовать обходные решения. Один из подходов заключается во включении указателя на приватные данные – обычно называемые `priv` – в структуру экземпляра. Приватную структуру можно объявить в публичном заголовочном файле, но определить только в файле реализации, что делает приватные данные непрозрачными для пользователей, но прозрачными для разработчика. Если приватная структура зарегистрирована в GType, она будет автоматически выделена объектной системой. Более того, даже не нужно включать указатель `priv`, если вы готовы использовать заклинание `G_TYPE_INSTANCE_GET_PRIVATE` каждый раз, когда требуются приватные данные. Для решения некоторых из этих сложностей существует несколько языков более высокого уровня, которые компилируются из исходного кода в исходный код GObject на C. Язык программирования Vala использует синтаксис в стиле C# и предварительно обрабатывается в обычный код C. GObject Builder, или GOB2, предлагает синтаксис шаблонов, напоминающий Java.

GOбъектная интроспекция

GObject introspection (сокращенно GIR) — это промежуточный слой интерфейса внешних функций между C-библиотеками (использующими GObject) и языковыми привязками, см. Список языковых привязок для GTK.

Использование

Комбинация C и GObject используется во многих успешных проектах свободного программного обеспечения, таких как рабочий стол GNOME, инструментарий GTK и программа для обработки изображений GIMP. Хотя многие приложения GObject написаны полностью на C, система GObject хорошо интегрируется с нативными объектными системами многих других языков, таких как C++, Java, Ruby, Python, Common Lisp и .NET/Mono. В результате, обычно относительно легко создавать языковые привязки для хорошо написанных библиотек, использующих фреймворк GObject. Однако написание GObject-кода непосредственно на C довольно многословно. Изучение библиотеки требует значительного времени, и программисты, имеющие опыт работы с высокоуровневыми объектно-ориентированными языками, часто находят работу с GObject на C несколько утомительной. Например, создание подкласса (даже просто подкласса GObject) может потребовать написания и/или копирования большого объема шаблонного кода. Однако использование Vala, языка, разработанного специально для работы с GObject и компилируемого в C, вероятно, упростит работу с GObject или разработку библиотек на основе GObject. Хотя они и не являются полноценными объектами первого класса (в GType нет фактических метатипов), метаобъекты, такие как классы и интерфейсы, создаются приложениями GObject во время выполнения и обеспечивают хорошую поддержку интроспекции. Возможности интроспекции используются языковыми привязками и приложениями для проектирования пользовательского интерфейса, такими как Glade, для выполнения таких задач, как загрузка общей библиотеки, предоставляющей класс GObject – обычно какой-то виджет в случае Glade – и последующее получение списка всех свойств класса, включая информацию о типе и строки документации.

Сравнения с другими объектами

Поскольку GObject предоставляет в основном полную объектную систему для C, его можно рассматривать как альтернативу языкам, производным от C, таким как C++ и Objective C. (Хотя оба также предлагают множество других возможностей, помимо своих соответствующих объектных систем.) Легко заметное различие между C++ и GObject заключается в том, что GObject (как и Java) не поддерживает множественное наследование. Использование функции выделения памяти `g_malloc` из GLib в GObject приведет к безусловному завершению программы при исчерпании памяти, в отличие от `malloc` библиотеки C, `new` в C++ и других распространенных распределителей памяти, которые позволяют программе справляться или даже полностью восстанавливаться после ситуаций нехватки памяти, не приводя к аварийному завершению. Это, как правило, препятствует включению GObject в программное обеспечение, где важна устойчивость к ограниченной памяти, или где обычно обрабатываются очень большое количество или очень большие объекты. `g_try_new` можно использовать, когда выделение памяти с большей вероятностью завершится неудачей (например, для большого объекта), но это не гарантирует, что выделение не потерпит неудачу в другом месте кода. Еще одно важное отличие заключается в том, что, хотя C++ и Objective C являются отдельными языками, GObject является строго библиотекой и, следовательно, не вводит нового синтаксиса или возможностей компилятора. Например, при написании кода на основе GObject на C часто необходимо выполнять явное приведение типов к базовому классу. Таким образом, "C с GObject", также называемый "C с добавлением GLib", рассматривается как язык, отличный от обычного C, и является строгим надмножеством обычного C — как и Objective C, но в отличие от C++. На платформах, где нет стандартного ABI, работающего со всеми компиляторами C++ (что обычно не так, поскольку обычно используются Itanium ABI или Microsoft ABI), библиотека, скомпилированная одним компилятором C++, не всегда может вызывать библиотеку, скомпилированную другим компилятором. Если требуется такая совместимость, методы C++ должны экспортироваться как обычные функции C, что частично сводит на нет цель объектной системы C++. Проблема возникает отчасти из-за того, что разные компиляторы C++ используют различные методы искажения имен для обеспечения уникальности всех экспортируемых символов. (Это необходимо, потому что, например, два разных класса могут иметь функции-члены с одинаковыми именами, одно имя функции может быть перегружено несколько раз, или функции с одинаковыми именами могут находиться в разных пространствах имен, но в объектном коде такие перекрытия недопустимы.) Напротив, поскольку C не поддерживает перегрузку или пространства имен, авторы библиотек C обычно используют явные префиксы для обеспечения глобальной уникальности экспортируемых ими имен. Таким образом, несмотря на то, что GObject ориентирован на объекты, библиотека на основе GObject, написанная на C, всегда будет использовать одни и те же внешние имена символов, независимо от используемого компилятора. Возможно, самое существенное отличие GObject — это акцент на сигналах (называемых событиями в других языках). Этот акцент обусловлен тем, что GObject был специально разработан для удовлетворения потребностей инструментария GUI. Хотя библиотеки сигналов существуют для большинства объектно-ориентированных языков, в случае GObject она встроена в объектную систему. Благодаря этому типичное приложение GObject будет использовать сигналы в гораздо большей степени, чем приложение, не использующее GObject, что делает компоненты GObject гораздо более инкапсулированными и пригодными для повторного использования, чем компоненты, использующие обычный C++ или Java. При использовании `glibmm/gtkmm`, официальных оберток C++ для Glib/GTK соответственно, родственный проект `libsigc++` позволяет легко использовать базовые сигналы GObject с использованием стандартного C++. Конечно, другие реализации сигналов доступны практически на всех платформах, хотя иногда требуется дополнительная библиотека, такая как Boost.Signals2 для C++.