El sistema de objetos GLib , o GObject , es una biblioteca de software libre que proporciona un sistema de objetos portátil e interoperabilidad transparente entre lenguajes. GObject está diseñado para usarse tanto directamente en programas C para proporcionar APIs basadas en C orientadas a objetos como a través de enlaces a otros lenguajes para proporcionar interoperabilidad transparente entre lenguajes, por ejemplo, PyGObject .
GObject, que depende únicamente de GLib y libc , es una piedra angular de GNOME y se utiliza en GTK , Pango , ATK y la mayoría de las bibliotecas de GNOME de nivel superior, como GStreamer y otras aplicaciones. Antes de GTK+ 2.0, el código similar a GObject formaba parte del código base de GTK. (El nombre “GObject” aún no se utilizaba; la clase base común se denominaba GtkObject
).
Con el lanzamiento de GTK+ 2.0, el sistema de objetos se extrajo en una biblioteca independiente debido a su utilidad general. En el proceso, la mayoría de las partes de la clase que no eran específicas de la GUIGtkObject
se trasladaron a GObject
, la nueva clase base común. La biblioteca GObject, que existe como biblioteca independiente desde el 11 de marzo de 2002 (la fecha de lanzamiento de GTK+ 2.0), ahora la utilizan muchos programas que no son de GUI, como aplicaciones de línea de comandos y de servidor .
Aunque GObject tiene su propio conjunto de documentación [1] y normalmente se compila en su propio archivo de biblioteca compartida , el código fuente de GObject reside en el árbol de fuentes de GLib y se distribuye junto con GLib. Por este motivo, GObject utiliza los números de versión de GLib y normalmente se empaqueta junto con GLib (por ejemplo, Debian incluye a GObject en su libglib2.0
familia de paquetes).
En el nivel más básico del marco de GObject se encuentra un sistema de tipos genérico y dinámico llamado GType. El sistema GType contiene una descripción en tiempo de ejecución de todos los objetos, lo que permite que el código de unión facilite la vinculación de múltiples lenguajes. El sistema de tipos puede manejar cualquier estructura de clase heredada individualmente , además de tipos no clasificados como punteros opacos , cadenas y números enteros y de punto flotante de diversos tamaños .
El sistema de tipos sabe cómo copiar, asignar y destruir valores que pertenecen a cualquiera de los tipos registrados. Esto es trivial para tipos como los enteros, pero muchos objetos complejos se cuentan por referencia , mientras que algunos son complejos pero no se cuentan por referencia. Cuando el sistema de tipos "copia" un objeto con recuento de referencia, normalmente solo aumentará su recuento de referencia, mientras que cuando copia un objeto complejo que no se cuenta por referencia (como una cadena), normalmente creará una copia real mediante la asignación de memoria .
Esta funcionalidad básica se utiliza para implementar GValue
, un tipo de contenedor genérico que puede contener valores de cualquier tipo conocido por el sistema de tipos. Dichos contenedores son particularmente útiles cuando se interactúa con entornos de lenguaje de tipado dinámico en los que todos los valores nativos residen en dichos contenedores etiquetados con tipos .
Los tipos que no tienen ninguna clase asociada se denominan tipos no clasificados . Estos tipos, junto con todos los tipos que corresponden a alguna forma de clase raíz, se conocen como tipos fundamentales : los tipos de los que se derivan todos los demás tipos. Estos forman un conjunto relativamente cerrado, pero aunque no se espera que el usuario medio cree sus propios tipos fundamentales, la posibilidad existe y se ha explotado para crear jerarquías de clases personalizadas , es decir, jerarquías de clases que no se basan en la GObject
clase.
A partir de GLib 2.9.2, [2] los tipos fundamentales integrados no clasificados son:
void
( G_TYPE_NONE
);char
, int
, long
y de 64 bits ( G_TYPE_CHAR
, G_TYPE_UCHAR
, G_TYPE_INT
, G_TYPE_UINT
, G_TYPE_LONG
, G_TYPE_ULONG
, G_TYPE_INT64
y G_TYPE_UINT64
);G_TYPE_BOOLEAN
);enum
, pero que difieren en que el último solo se utiliza para campos de bits ( G_TYPE_ENUM
y G_TYPE_FLAGS
);float
y double
( G_TYPE_FLOAT
y G_TYPE_DOUBLE
);char *
( G_TYPE_STRING
);void *
( G_TYPE_POINTER
) de C.Los tipos fundamentales integrados clasificados son:
GObject
, la raíz del árbol de herencia de clase estándar ( G_TYPE_OBJECT
)G_TYPE_INTERFACE
)G_TYPE_BOXED
)G_TYPE_PARAM
).Los tipos que pueden ser instanciados automáticamente por el sistema de tipos se denominan "instantables" . Una característica importante de estos tipos es que los primeros bytes de cualquier instancia siempre contienen un puntero a la estructura de clase (una forma de tabla virtual ) asociada al tipo de la instancia. Por esta razón, cualquier tipo instanciable debe ser clasificado. Por el contrario, cualquier tipo no clasificado (como entero o cadena ) debe ser no instanciable. Por otro lado, la mayoría de los tipos clasificados son instanciables, pero algunos, como los tipos de interfaz, no lo son.
Los tipos que se derivan de los tipos fundamentales incorporados de GObject se dividen aproximadamente en cuatro categorías:
enum
tipo) que se desee utilizar de alguna manera relacionada con el sistema de objetos (por ejemplo, como el tipo de una propiedad de objeto) debe registrarse en el sistema de tipos. Normalmente, el código de inicialización que se encarga de registrar estos tipos se genera mediante una herramienta automatizada denominada glib-mkenums
[3] y se almacena en un archivo independiente.background-color
propiedad, cuyos valores deberían ser instancias de una estructura que se parece a . Para evitar tener que crear una subclase de , podemos crear un tipo encajonado para representar esta estructura y proporcionar funciones para copiar y liberar. GObject se entrega con un puñado de tipos encajonados que envuelven tipos de datos GLib simples. Otro uso para los tipos encajonados es como una forma de envolver objetos externos en un contenedor etiquetado que el sistema de tipos puede identificar y sabrá cómo copiar y liberar.struct color { int r, g, b; }
GObject
G_TYPE_POINTER
), a menudo es una buena idea crear un tipo de puntero derivado, documentando el hecho de que los punteros deben hacer referencia a un tipo particular de objeto, aunque no se diga nada más al respecto.GObject
. También existen interfaces que, a diferencia de las interfaces clásicas de estilo Java , pueden contener métodos implementados. Por lo tanto, las interfaces GObject pueden describirse como mixins .El sistema de mensajería GObject consta de dos partes complementarias: cierres y señales .
Cada clase GObject se implementa mediante al menos dos estructuras: la estructura de clase y la estructura de instancia .
Definir una clase en el marco de GObject es complejo y requiere grandes cantidades de código repetitivo , como definiciones manuales de macros de conversión de tipos y encantamientos de registro de tipos poco claros. Además, dado que una estructura de C no puede tener modificadores de acceso como “público”, “protegido” o “privado”, se deben utilizar soluciones alternativas para proporcionar encapsulación . Un enfoque es incluir un puntero a los datos privados (llamados convencionalmente _priv
) en la estructura de instancia. La estructura privada se puede declarar en el archivo de encabezado público, pero definir solo en el archivo de implementación, con el efecto de que los datos privados son opacos para los usuarios, pero transparentes para el implementador. Si la estructura privada se registra con GType, el sistema de objetos la asignará automáticamente. De hecho, ni siquiera es necesario incluir el _priv
puntero, si uno está dispuesto a usar el encantamiento G_TYPE_INSTANCE_GET_PRIVATE
cada vez que se necesitan los datos privados.
Para abordar algunas de estas complejidades, existen varios lenguajes de alto nivel que compilan de fuente a fuente en GObject en C. El lenguaje de programación Vala utiliza una sintaxis de estilo C# y se preprocesa en código C básico . GObject Builder, o GOB2, ofrece una sintaxis de plantilla que recuerda a Java .
La combinación de C y GObject se utiliza en muchos proyectos de software libre exitosos , como el escritorio GNOME , el kit de herramientas GTK y el programa de manipulación de imágenes GIMP .
Aunque muchas aplicaciones GObject están escritas completamente en C, el sistema GObject se integra bien con los sistemas de objetos nativos de muchos otros lenguajes, como C++ , Java , Ruby , Python , Common Lisp y .NET / Mono . Como resultado, suele ser relativamente sencillo crear enlaces de lenguaje para bibliotecas bien escritas que utilizan el marco GObject.
Sin embargo, escribir código GObject en C es relativamente complejo. La biblioteca requiere mucho tiempo para aprenderse y los programadores con experiencia en lenguajes orientados a objetos de alto nivel a menudo encuentran algo tedioso trabajar con GObject en C. Por ejemplo, crear una subclase (incluso una subclase de GObject
) puede requerir escribir y/o copiar grandes cantidades de código repetitivo . [5] Sin embargo, es probable que usar Vala , un lenguaje que está diseñado principalmente para trabajar con GObject y que convierte a C, haga que trabajar con GObject o escribir bibliotecas basadas en GObject sea más agradable.
Aunque no son realmente objetos de primera clase (no hay metatipos reales en GType), los metaobjetos como las clases y las interfaces son creados por aplicaciones GObject en tiempo de ejecución y proporcionan un buen soporte para la introspección . Las capacidades introspectivas son utilizadas por enlaces de lenguaje y aplicaciones de diseño de interfaz de usuario como Glade para permitir hacer cosas como cargar una biblioteca compartida que proporciona una clase GObject (normalmente algún tipo de widget , en el caso de Glade) y luego obtener una lista de todas las propiedades de la clase, completa con información de tipo y cadenas de documentación.
Dado que GObject proporciona un sistema de objetos casi completo para C [ cita requerida ] , puede verse como una alternativa a los lenguajes derivados de C, como C++ y Objective-C . (Aunque ambos también ofrecen muchas otras características más allá de sus respectivos sistemas de objetos). Una diferencia fácilmente observable entre C++ y GObject es que GObject (como Java) no admite la herencia múltiple . [6]
El uso de la función de asignación de memoria g_malloc() de GLib por parte de GObject hará que el programa salga incondicionalmente cuando se agote la memoria, a diferencia de malloc () de la biblioteca C, new de C++ y otros asignadores de memoria comunes que permiten que un programa se enfrente o incluso se recupere por completo de situaciones de falta de memoria sin simplemente bloquearse. [7] Esto tiende a funcionar en contra de la inclusión de GObject en software donde la resiliencia frente a una memoria limitada es importante, o donde se manejan comúnmente muchos objetos o muy grandes. g_try_new() se puede utilizar cuando es más probable que falle una asignación de memoria (para un objeto grande, por ejemplo), pero esto no puede garantizar que la asignación no falle en otra parte del código. [8]
Otra diferencia importante es que, mientras que C++ y Objective-C son lenguajes separados, GObject es estrictamente una biblioteca y, como tal, no introduce ninguna sintaxis nueva ni inteligencia de compilación. Por ejemplo, al escribir código C basado en GObject, con frecuencia es necesario realizar una conversión ascendente explícita . [ cita requerida ] Por lo tanto, “C con GObject”, también llamado “C con sabor a simplicidad”, considerado como un lenguaje separado de C simple, es un superconjunto estricto de C simple, como Objective C, pero diferente de C++.
En plataformas donde no hay una ABI estándar que funcione en todos los compiladores de C++ (lo que no suele ser el caso, ya que normalmente se sigue la ABI de Itanium o la ABI de Microsoft), una biblioteca compilada con un compilador de C++ no siempre puede llamar a una biblioteca compilada con uno diferente. [ cita requerida ] Si se requiere dicha compatibilidad, los métodos de C++ deben exportarse como funciones de C simples, lo que frustra en parte el propósito del sistema de objetos de C++. [ cita requerida ] El problema ocurre en parte porque los diferentes compiladores de C++ usan diferentes tipos de manipulación de nombres para garantizar la unicidad de todos los símbolos exportados. (Esto es necesario porque, por ejemplo, dos clases diferentes pueden tener funciones miembro con nombres idénticos, un nombre de función puede sobrecargarse varias veces o funciones con nombres idénticos pueden aparecer en diferentes espacios de nombres , pero en el código objeto estas superposiciones no están permitidas). [ cita requerida ] Por el contrario, dado que C no admite ninguna forma de sobrecarga o espacio de nombres, los autores de bibliotecas de C normalmente usarán prefijos explícitos para garantizar la unicidad global de sus nombres exportados. [ cita requerida ] Por lo tanto, a pesar de estar orientada a objetos, una biblioteca basada en GObject escrita en C siempre usará los mismos nombres de símbolos externos independientemente del compilador que se use.
Tal vez la diferencia más profunda es el énfasis de GObject en las señales (llamadas eventos en otros lenguajes). [ cita requerida ] Este énfasis se deriva del hecho de que GObject fue diseñado específicamente para satisfacer las necesidades de un conjunto de herramientas GUI. Si bien existen bibliotecas de señales para la mayoría de los lenguajes orientados a objetos, en el caso de GObject está integrado en el sistema de objetos. Debido a esto, una aplicación GObject típica tenderá a usar señales en una medida mucho mayor que una aplicación que no sea GObject, lo que hace que los componentes GObject sean mucho más encapsulados y reutilizables que los que usan C++ o Java simples. [ cita requerida ] [ ¿según quién? ] Si se usa glibmm/ gtkmm , los envoltorios oficiales de C++ para Glib/GTK respectivamente, el proyecto hermano libsigc++ permite un uso fácil de las señales GObject subyacentes usando C++ estándar. Por supuesto, hay otras implementaciones de señales disponibles en casi todas las plataformas, aunque a veces se necesita una biblioteca adicional, como Boost.Signals2 para C++.