2006-09-08 German Poo-Caaman~o <gpoo@ubiobio.cl>
* ChangeLog: Added itself.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"/usr/share/sgml/docbook/dtd/xml/4.1.2/docbookx.dtd" [
<!ENTITY Emacs "<application>Emacs</application>">
<!ENTITY Vim "<application>Vim</application>">
<!ENTITY ChangeLog "<filename>ChangeLog</filename>">
]>
<article class="techreport" id="index" lang="es">
<articleinfo>
<title>Guía de programación de GNOME</title>
<authorgroup>
<author>
<firstname>Federico</firstname>
<surname>Mena Quintero</surname>
<affiliation>
<address>
<email>federico@gnu.org</email>
</address>
</affiliation>
</author>
<author>
<firstname>Miguel</firstname>
<surname>de Icaza</surname>
<affiliation>
<address>
<email>miguel@kernel.org</email>
</address>
</affiliation>
</author>
<author>
<firstname>Morten</firstname>
<surname>Welinder</surname>
<affiliation>
<address>
<email>terra@diku.dk</email>
</address>
</affiliation>
</author>
<othercredit>
<firstname>Germán</firstname>
<surname>Poó Caamaño</surname>
<contrib>Traducción al español</contrib>
<affiliation>
<address>
<email>gpoo@ubiobio.cl</email>
</address>
</affiliation>
</othercredit>
<othercredit>
<firstname>Lucas</firstname>
<surname>Vieites Fariña</surname>
<contrib>Revisión de la traducción al español</contrib>
<affiliation>
<address>
<email>lucas@asixinformatica.com</email>
</address>
</affiliation>
</othercredit>
</authorgroup>
<copyright>
<year>2000</year>
<holder>The Free Software Foundation</holder>
</copyright>
<copyright>
<year>2004—2006</year>
<holder>GNOME Foundation, respecto de la versión en español.</holder>
</copyright>
<abstract>
<para>
Este artículo contiene varías guías y sugerencias para los
programadores de GNOME, así como ciertas políticas que
deberían seguirse cuando se escriben programas para GNOME.
Es un intento para que los programadores puedan aprender
acerca del proceso de desarrollo de GNOME y su filosofía.
GNOME es un esfuerzo de equipo, así que será útil para
que los programadores sepan «la forma de hacer la
cosas».
</para>
</abstract>
</articleinfo>
<!-- Introduction -->
<sect1 id="intro">
<title>Introducción</title>
<para>
GTK+, la biblioteca de interfaz de usuario básica de GNOME, nos
ha enseñado algunas lecciones importantes en el diseño de software.
El código de GTK+ es limpio, consistente, mantenible y tiene
sentido. Tal código no sólo provoca placer al trabajar con el,
sino que además es un incentivo para las buenas prácticas de
programación para aquellos que quieran extenderlo y modificarlo.
</para>
<para>
En este artículo intentamos presentar algunas sugerencias y
lineamientos que deberías tener en cuenta cuando escribas código
para el proyecto GNOME. Presentamos algunas de las políticas
que deben seguirse cuando se modifica el código de otras
personas, usando el repositorio CVS y asegurándose de que el
código se ajusta para ser incluído en GNOME. También
presentamos información que será útil para los mantenedores
de paquetes.
</para>
<para>
Además de este documento, asegúrate de leer los Estándares de
Programación de GNU. Estos se encuentran disponibles en el
nodo info <filename>(Standards)</filename> en la documentación
estándar de GNU.
</para>
</sect1>
<!-- The Importance of Writing Good Code -->
<sect1 id="good-code">
<title>La importancia de escribir buen código</title>
<para>
GNOME es un proyecto de software libre muy ambicioso y, como tal,
está compuesto por muchos paquetes de software que son más o menos
independientes el uno del otro. Mucho del trabajo en GNOME lo
realizan voluntarios; programadores que vienen y se van
en cualquier momento y que serán capaces de dedicar
diferentes cantidades de tiempo al proyecto GNOME.
Muchas personas trabajan en software libre en su tiempo libre o
como un pasatiempo, así, si sus responsabilidades del ‘mundo
real’ cambian, se verá reflejado en la cantidad de trabajo que
dedicarán a proyectos de software libre.
</para>
<para>
Se tarda mucho tiempo en escribir software y supone una
gran cantidad de trabajo. Es por esto que muchos
de los voluntarios de tiempo parcial no pueden comenzar
grandes proyectos por sí mismos; es mucho más fácil y
gratificante contribuir a proyectos existentes, puesto que
los resultados son inmediatamente visibles y usables.
</para>
<para>
Teniendo esto en cuenta y el hecho que los programadores de
software libre tiene recursos escasos, concluimos que
para los proyectos existentes es muy importante facilitar,
tanto como sea posible, que otras personas puedan contribuir.
Una forma de hacerlo es asegurándose de que los programas
sean fáciles de leer, entender y modificar.
</para>
<para>
El código desordenado es difícil de leer y las personas pueden
perder interés si no son capaces de descifrar lo que el código
intenta hacer. Además, es importante que los programadores sean
capaces de entender el código rápidamente, y así poder comenzar
a contribuir reparando fallos y extendiéndolo en un período breve
de tiempo. El código fuente es una forma de comunicación, y así
como a alguien podría no querer leer una novela con errores
ortográficos y mala puntuación, los programadores deben intentar
escribir buen código de tal forma que sea fácil de entender y
modificar por otros.
</para>
<para>
Existen algunas cualidades que son importantes en un buen
código y por qué son importantes para los desarrolladores
de software libre:
<variablelist>
<varlistentry>
<term>Limpieza</term>
<listitem>
<para>
Un código limpio es fácil de leer; permite a las
personas leerlo con un mínimo esfuerzo y así pueden
entenderlo más fácilmente.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Consistencia</term>
<listitem>
<para>
El código consistente permite más fácilmente que las
personas entiendan como funciona el programa; cuando se
lee código consistente, subconcientemente uno se forma
un número de supuestos y expectativas acerca del
funcionamiento del código, de esta forma es más fácil
y seguro realizarle modificaciones.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Extensibilidad</term>
<listitem>
<para>
El código de propósito general es más fácil de reutilizar
y modificar que el código demasiado específico con muchos
supuestos escritos directamente en el código (hardcoded).
Cuando alguien desea agregar una nueva característica
a un programa, obviamente será más fácil hacerlo si el
código fue diseñado para ser extensible desde el inicio.
El código que no fue escrito de esta forma hará que las
personas deban implementar hacks muy feos para poder
añadir características.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Corrección</term>
<listitem>
<para>
Finalmente, el código diseñado debe ser correcto para
que las personas pierdan menos tiempo preocupándose de
los errores y se ocupen en extender las
características de un programa. Los usuarios también
apreciarán un código correcto, ya que a nadie le gusta
que un programa se caiga.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
En resumen, los programadores a menudo contribuyen en
su tiempo libre a proyectos de software libre, y aún quienes
contribuyen de forma regular pueden detenerse en cualquier
instante del tiempo, así que es muy importante que el código
sea bueno y les permita modificarlo facilmente. El resultado
final será un mejor software al que los programadores
querrán extender.
</para>
</sect1>
<!-- Coding Style -->
<sect1 id="code-style">
<title>Estilo de programación</title>
<para>
‘El estilo de programación’ se refiere a la forma en
que se da formato al código fuente. Para C, esto involucra
la forma en que se ubican las llaves, se indenta el código y
se utilizan los paréntesis. GNOME tiene una mezcla de estilos
de programación y no se obliga el uso de ninguno de ellos. Lo más
importante es que el código sea consistente dentro de un programa
o una biblioteca — el código con un formato desordenado
no es aceptable debido a que es difícil de leer.
</para>
<para>
Cuando escribas un nuevo programa o biblioteca, sigue un
estilo consistente de ubicación de llaves y de indentación.
Si no tienes ninguna preferencia personal de estilo,
recomendamos el estilo de programación del núcleo de Linux o
el estilo de programación de GNU.
</para>
<para>
Lee el nodo de info <filename>(Standards)Writing C</filename>
en la documentación de GNU. Luego, obtén el código fuente
de Linux y lee el archivo
<filename>linux/Documentation/CodingStyle</filename>, e
ignora los chistes de Linus. Estos dos documentos te darán
una buena idea de nuestras recomendaciones para el código de
GNOME.
</para>
<!-- Indentation Style -->
<sect2 id="indent">
<title>Estilo de indentación</title>
<para>
Para el código del núcleo de GNOME preferimos el estilo
de indentación del núcleo de Linux. Usa tabuladores
de 8 espacios para la indentación.
</para>
<para>
Usar tabuladores de 8 espacios para indentación proporciona
un número de beneficios. Permite que el código sea más
fácil de leer, ya que la indentación se marca claramente.
También ayuda a mantener el código ordenado
forzando a dividir funciones en trozos más modulares y
bien definidos — si la indentación va más allá del
margen derecho, significa que la función está mal
diseñada y que debiera dividirse para hacerla más modular
o bien, repensarla.
</para>
<para>
Los tabuladores de 8 espacios para indentación también
ayudan al diseño de funciones que encajen bien en la pantalla,
lo cual significa que las personas puedan entender el código
sin tener que desplazarse atrás y adelante para entenderlo.
</para>
<para>
Si usas &Emacs;, entonces puedes seleccionar el estilo de
indentación del núcleo de Linux incluyendo en el
archivo <filename>.emacs</filename> lo siguiente:
<programlisting>
(add-hook 'c-mode-common-hook
(lambda ()
(c-set-style "k&r")
(setq c-basic-offset 8)))</programlisting>
En los nuevos Emacs o con el nuevo cc-mode, puedes ser capaz
de hacerlo más simple con:
<programlisting>
(add-hook 'c-mode-common-hook
(lambda ()
(c-set-style "linux")))</programlisting>
</para>
<para>
El estilo de indentación de GNU es el predeterminado para
&Emacs;, así que no es necesario agregar nada en el
archivo <filename>.emacs</filename> para habilitarlo.
Si deseas seleccionarlo explícitamente, sustituye
«gnu» por «linux» en el ejemplo anterior.
</para>
<para>
Si usas &Vim;, entonces puedes seleccionar el estilo
de indentación del núcleo de Linux incluyendo el
siguiente fragmento en el archivo
<filename>.vimrc</filename>:
<programlisting>
set ts=8
if !exists("autocommands_loaded")
let autocommands_loaded = 1
augroup C
autocmd BufRead *.c set cindent
augroup END
endif</programlisting>
</para>
<para>
Como alternativa puedes seleccionar el estilo de
indentación de GNU en &Vim; usando lo siguiente en
el archivo <filename>.vimrc</filename>:
<footnote>
<para>
Gracias a Tomas Ögren por proporcionar este código.
</para>
</footnote>:
<programlisting>
augroup C
autocmd BufRead *.c set cinoptions={.5s,:.5s,+.5s,t0,g0,^-2,e-2,n-2,p2s,(0,=.5s formatoptions=croql cindent shiftwidth=4 tabstop=8
augroup END</programlisting>
</para>
<note>
<para>
Si sabe personalizar el estilo de indentación en otros
editores populares, por favor háganoslo saber y así podemos
expandir este documento.
</para>
</note>
</sect2>
<!-- Naming Conventions -->
<sect2 id="naming">
<title>Convenciones de nombres</title>
<para>
Es importante seguir una buena convención de nombres para
los símbolos de los programas. Es específicamente
importante para las bibliotecas, ya que no debería
ensuciarse el espacio de nombres global — es muy
molesto cuando una biblioteca tiene símbolos nombrados
desordenadamente que chocan con nombres que pueda
querer usar en sus programas.
</para>
<para>
Los nombres de las funciones deberían ser de la forma
<function>modulo_submodulo_operacion</function>, por ejemplo,
<function>gnome_canvas_set_scroll_region</function> o
<function>gnome_mime_get_keys</function>. Esta convención
elimina las colisiones de nombres de símbolos entre módulos.
Es muy importante para las bibliotecas.
</para>
<para>
Los símbolos deben tener nombres descriptivos. Como Linus
dice, no use <function>cntusr()</function>, sino que use
<function>count_active_users()</function>. Esto permite
que el código sea más fácil de leer y casi se auto
documenta.
</para>
<para>
Intente usar las mismas convenciones de nombre que tienen
GTK+ y las bibliotecas de GNOME:
<itemizedlist>
<listitem>
<para>
Los nombres de las funciones en minúsculas, con líneas
de subrayado para separar palabras, tal como:
<function>gnome_canvas_set_scroll_region()</function>,
<function>gnome_mime_get_keys()</function>.
</para>
</listitem>
<listitem>
<para>
Las macros y las enumeraciones en mayúsculas, con líneas
de subrayado para separar palabras, tal como:
<symbol>GNOMEUIINFO_SUBTREE()</symbol> para una macro y
<symbol>GNOME_INTERACT_NONE</symbol> para un valor
enumerado.
</para>
</listitem>
<listitem>
<para>
Los nombres de tipos y estructuras usan una mezcla de
mayúsculas y minúsculas, tal como:
<symbol>GnomeCanvasItem</symbol>,
<symbol>GnomeIconList</symbol>.
</para>
</listitem>
</itemizedlist>
Al utilizar líneas de subrayado para separar palabras el
código estará menos apretado y facilita la edición, ya
que puede usar las secuencias de teclas que permiten
navegar entre palabras más rápidamente en cualquier editor.
</para>
<para>
Si estás escribiendo una biblioteca, entonces puedes necesitar
exportar símbolos que serán usados sólo dentro de la
biblioteca. Por ejemplo, dos de los archivos objeto que
componen la biblioteca <filename>libfoo.so</filename> pueden
requerir acceder a símbolos ubicados en el otro archivo,
pero se tiene la intención que éstos símbolos sean
utilizados desde los programas de usuario. En este caso,
coloca una línea de subrayado antes del nombre de la
función y haz que la primera palabra siga la convención
estándar módulo/submódulo. Por ejemplo, podrías tener
una función llamada
<function>_foo_internal_frobnicate()</function>.
</para>
<!-- Consistency in Naming -->
<sect3 id="consist">
<title>Consistencia entre nombres</title>
<para>
Es importante que las variables se nombren de manera
consistente. Por ejemplo, un módulo que manipula una
lista puedes elegir nombrar las variables que mantienen
un puntero a la lista como «<symbol>l</symbol>»,
por elegancia y simplicidad. Sin embargo, es importante que
un módulo que manipula widgets y tamaños no use variables
llamadas «<symbol>w</symbol>» tanto para
widgets y anchos («width») (como en valores de ancho/alto);
esto podría hacer que el código sea inconsistente
y difícil de leer.
</para>
<para>
Por supuesto, nombre muy cortos y elegantes solamente deberían
ser usados para variables locales de funciones.
Nunca llame una variable global «<symbol>x</symbol>»; use
un nombre más largo que indique lo que significa.
</para>
</sect3>
</sect2>
<!-- Cleanliness -->
<sect2 id="clean">
<title>Limpieza</title>
<para>
El código de GNOME debe ser tan limpio como sea posible.
Esto implica usar un estilo de indentación consistente
y una buena convención para nombrar símbolos, como se
ha indicado anteriormente. Esto también implica lo
siguiente.
</para>
<para>
Aprender el uso correcto de la palabra reservada
<symbol>static</symbol>.
<emphasis>No</emphasis> declarar todos los símbolos como
globales. Esto tiene la ventaja de poder usar nombres
más cortos dentro de las funciones en un sólo archivo
fuente, ya que no son globalmente visibles y por
consiguiente no necesitas emplear el prefijo
módulo/submódulo.
</para>
<para>
Aprender el uso correcto de la palabra reservada
<symbol>const</symbol>. Úsala consistentemente,
así permitirás al compilador que atrape muchos
errores estúpidos.
</para>
<para>
Si tienes una función que retorna un puntero a
un dato interno que se supone que el usuario
no debe liberar, deberías usar el modificador
<symbol>const</symbol>. Este avisará al usuario si intenta
hacer alguna operación incorrecta, por ejemplo:
<programlisting>
const char *gnome_mime_get_info (const char *info);</programlisting>
El compilador avisará si el usuario intenta liberar
la cadena retornada. Esto puede atrapar muchos
errores.
</para>
<para>
Si tienes «valores mágicos» en el programa o
biblioteca, usa macros que los definan en vez de usarlos
directamente en el código:
<programlisting>
/* Amount of padding for GUI elements */
#define GNOME_PAD 8
#define GNOME_PAD_SMALL 4
#define GNOME_PAD_BIG 12</programlisting>
</para>
<para>
Si tienes una lista de valores posibles para una variable,
no uses macros para ellas, usa enum para darle
un nombre de tipo — esto permite disponer de nombres
simbólicos en un depurador. Además, no uses
«int» para almacenar un valor enumerado; usa
el tipo enum. Esto le permite al compilador atrapar
los errores por tí, permitiéndole al depurador mostrar los
valores apropiados y hacer obvios los valores que una
variable puede tomar. A continuación un ejemplo:
<programlisting>
/* Shadow types */
typedef enum {
GTK_SHADOW_NONE,
GTK_SHADOW_IN,
GTK_SHADOW_OUT,
GTK_SHADOW_ETCHED_IN,
GTK_SHADOW_ETCHED_OUT
} GtkShadowType;
void gtk_frame_set_shadow_type (GtkFrame *frame, GtkShadowType type);</programlisting>
</para>
<para>
Si define un conjunto de valores para un campo de bits, haz
lo siguiente:
<programlisting>
<![CDATA[
/* Update flags for items */
enum {
GNOME_CANVAS_UPDATE_REQUESTED = 1 << 0,
GNOME_CANVAS_UPDATE_AFFINE = 1 << 1,
GNOME_CANVAS_UPDATE_CLIP = 1 << 2,
GNOME_CANVAS_UPDATE_VISIBILITY = 1 << 3,
GNOME_CANVAS_UPDATE_IS_VISIBLE = 1 << 4
};]]></programlisting>
Esto hace más fácil modificar la lista de valores y menos
propenso a error que especificando los valores a mano. También
permite usar estos valores como símbolos en un depurador.
</para>
<para>
No escribas código ofuscado, intenta que sea espartano.
Para clarificar una expresión, no uses más paréntesis que
los necesarios. Usa espacios antes de los paréntesis y
después de las comas y también alrededor de los operadores
binarios.
</para>
<para>
No escribas hacks en el código. En vez de escribir un
hack feo, reescribe el código para que quede limpio,
extensible y mantenible.
</para>
<para>
Asegúrate de que el código compila absolutamente sin ningún
aviso del compilador. Esto te ayudará a atrapar errores
estúpidos. Usa los prototipos de las funciones en los
archivos de encabezados de forma consistente.
</para>
<para>
Dentro de GNOME puedes usar la macro de Autoconf
<symbol>GNOME_COMPILE_WARNINGS</symbol> en el archivo
<filename>configure.in</filename>. Esto permitirá contar
con un buen conjunto de avisos del compilador de una manera
portable.
</para>
<para>
Comenta el código. Coloca comentarios antes de
cada función para decir que hace. No digas cómo lo hace
a menos que sea absolutamente necesario; debería ser
obvio al leer el código. Si no lo fuera, entonces
puedes desear reescribirla hasta que sea fácil de
entender.
</para>
<para>
Cuando documentes las funciones de la API de una biblioteca,
sigue las directrices indicadas en el archivo
<filename>gnome-libs/devel-docs/api-comment-style.txt</filename>.
Esto permite que el código fuente pueda proporcionar documentación
en línea, que posteriormente se extrae mediante el sistema
<application>gtk-doc</application> para crear un manual DocBook
de forma automática.
</para>
<!-- Portability considerations -->
<sect3 id="portability">
<title>Consideraciones de portabilidad</title>
<para>
Se construye GNOME en muchas plataformas diferentes.
Se puede asumir que serán plataformas más o menos tipo Unix;
hasta el momento GNOME no ha sido portado a sistema no-Unix,
así que se puede asumir que los servicios estándares de
Unix estarán disponibles.<footnote>
<para>
¿Servicios estándar de Unix? Por supuesto que estamos
bromeando.
</para>
</footnote>
</para>
<para>
Recuerda que el mundo no es tu propio equipo con GNU/Linux;
las gente realmente usa otros tipos de máquinas.
</para>
<para>
Intenta no usar extensiones específicas de
<application>GCC</application> debido a que éstas no
funcionarán con otros compiladores. Si realmente debes
hacer uso de tal cosa, ve la forma en que se hace
en <application>Glib</application> con el conjunto de
macros G_GNUC; asegúrate también de incluir código
que funcione con compiladores ISO C. Si sólo tienes
disponible <application>GCC</application>, aprende a usar
las opciones <option>-ansi -pedantic</option> que
permiten probar código sospechoso.
</para>
<para>
Recuerda que algunas plataformas no disponen de
<application>GCC</application> o que
<application>GDB</application> puede ser inusable en ellos,
y se querrán usar otros compiladores y depuradores.
</para>
</sect3>
<!-- GTK+-related Issues -->
<sect3 id="gtk">
<title>Tópicos relacionados con GTK+</title>
<para>
GTK+ permite hacer mucha magia y ofuscación con manejadores
de señal, pasar cerraduras y conjuntos de datos. Si te
encuentras utilizando muchos
<function>gtk_object_set_data()</function> en un mismo lugar,
o estas pasando estados de forma extraña a través de manejadores
de señales, reescribe el código. Si necesitas adjuntar muchos
datos a un objeto en particular, entonces es un buen candidato
para una nueva clase derivada, que no sólo hará al código
más limpio, sino que también lo hará más extensible.
</para>
<para>
Mucha de la heurística en manejadores de eventos complicadas
a menudo se pueden reemplazar limpiando el código a través
de una máquina de estados. Esto es útil cuando se quieren
implementar cosas truculentas como selección y comportamientos
de arrastrado, y hará al código más fácil de depurar y extender.
</para>
</sect3>
</sect2>
</sect1>
<!-- Correctness and Robustness -->
<sect1 id="robust">
<title>Corrección y robustez</title>
<para>
Es extremadamente importante que el código de GNOME sea
correcto y robusto. Esto significa que el código debería hacer
lo que se espera que haga y debería manejar bien las condiciones
de excepción. Aunque esto pueda parecer obvio, esta sección
dará algunas ideas para asegurar la corrección de tu código
de GNOME. Esto es muy importante, ya que los usuarios esperan
y merecen un software confiable que se ejecute correctamente
y que no se caiga.
</para>
<!-- Ensuring Consistency -->
<sect2 id="ensure">
<title>Cómo asegurar la consistencia</title>
<para>
<!-- FIXME: Buscar un mejor termino para "assertion" -->
Utiliza las macros de aserción de Glib para asegurarte
que el estado de un programa es consistente. Estas macros
ayudan a localizar errores muy rápidamente y se gastará
mucho menos tiempo en el depurado si se emplean de forma
generosa y consistente.
</para>
<para>
Inserta verificaciones de sanidad en el código en puntos
importantes como es el inicio de funciones públicas, al
final de código que realiza una búsqueda que siempre
debe ser exitosa y en cualquier lugar donde
el rango de valores calculados es importante.
</para>
</sect2>
<!-- Assertions and Preconditions -->
<sect2 id="assert">
<title>Aserciones y precondiciones</title>
<para>
Las aserciones y precondiciones ayudan a asegurar que
el estado de un programa es consistente. Glib proporciona
macros para colocar aserciones y precondiciones en el
código. Deberías usarlas libremente; a cambio podrás
localizar errores muy rápidamente y ocuparás menos tiempo
rastreando errores con el depurador.
</para>
<para>
Existen macros de Glib para precondiciones, las cuales
emiten un mensaje cuando una condición falla y retornan
de la función desde donde fueron llamadas. Debieran ser
usadas en el inicio de las funciones.
<variablelist>
<varlistentry>
<term><function>g_return_if_fail
(condición)</function></term>
<listitem>
<para>
Retorna desde la línea actual de la función si la
<symbol>condición</symbol> es falsa.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>g_return_val_if_fail (condición,
valor)</function></term>
<listitem>
<para>
Retorna el <symbol>valor</symbol> indicado desde
la función actual si la <symbol>condición</symbol>
es falsa.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
También existen macros para aserciones. Estas emitirán
un mensaje cuando una condición falle y llamará a la
función <function>abort(3)</function> para terminar el
programa. Debieran ser usadas para asegurarse la
consistencia para códigos internos.
<variablelist>
<varlistentry>
<term><function>g_assert (condición)</function></term>
<listitem>
<para>
Aborta el programa si la <symbol>condición</symbol>
es falsa.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>g_assert_not_reached ()</function></term>
<listitem>
<para>
Aborta el programa si se llama a la macro.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Estas funciones debieran emplearse para imponer
precondiciones en el código y verificar su corrección
— piensa en ellas como verificaciones de sanidad
en los programas. Debieras usarlas libremente como
asistencia para atrapar los errores rápidamente; una
vez que el programa se encuentre completamente depurado,
puedes compilarlo con estas macros deshabilitadas y así
evitar añadirle sobrecarga al momento de ejecutarlos.
</para>
<para>
Las macros <function>g_return_*()</function> debieran
emplearse al inicio de las funciones públicas de las
bibliotecas, para asegurarse de que los argumentos que se pasan
a ellas sean correctos y tengan un rango válido. Si
una función no retorna valor (por ejemplo, retorna
<symbol>void</symbol>), debieras usar
<function>g_return_if_fail()</function>. En caso contrario,
debiera usar <function>g_return_val_if_fail()</function>
para retornar un valor ‘seguro’. Cuando
se invoca una función de biblioteca que usa estas macros
con un argumento incorrecto , se producirá un
mensaje de error y continuará la ejecución. El
programa cliente podrá tener algún sobresalto, hacer
nada o caerse, pero al menos sabrá
<emphasis>dónde</emphasis> se le ha pasado un valor
incorrecto a la función.
</para>
<para>
La macro <function>g_assert()</function> debiera usarse para
asegurar la consistencia interna de una biblioteca o
programa. En vez de retornar de la función y continuar
la ejecución si la condición falla,
<function>g_assert()</function> producirá en mensaje de error
e inmediatamente abortará el programa. Esto es para
evitar que el programa continúe ejecutándose en un
estado inconsistente. Debieras usar esta macro para
asegurarte de que el programa o biblioteca está usando
valores internos sanos.
</para>
<para>
La macro <function>g_assert_not_reached()</function> se
usa para marcar el lugar en el código que nunca
debiera producirse. Por ejemplo, si tienes una
cláusula <symbol>switch</symbol> y piensas que manejas
todos los valores posibles en las etiquetas
<symbol>case</symbol>, entonces debieras colocar
<function>g_assert_not_reached()</function> en la
etiqueta <symbol>default</symbol> para asegurarte de que
el código nunca llegue allá (podría significar que
ha perdido algún valor o que el programa se encuentra
incorrecto).
</para>
<para>
Estas macros ayudan a encontrar errores más rápido
a través de avisos que se producen tan pronto como
el programa alcanza un estado incosistente. Úsalos
frecuentemente y encontrarás muchos errores más
fácilmente.
</para>
</sect2>
<!-- GTK+-related Issues -->
<sect2 id="assert-gtk">
<title>Tópicos relacionados con GTK+</title>
<para>
Debe ser cuidadoso cuando escribas manejadores de eventos
— asegurate de que los eventos son manipulados en
las situaciones apropiadas. Es necesario asegurarse de
que los manejadores de señales tengan los prototipos
correctos. Esto es muy importante. Recuerda que no
todos los prototipos de manejadores de señal se parecen
a lo siguiente:
<programlisting>
static void my_handler (GtkWidget *widget, gpointer data);</programlisting>
Por ejemplo, los manejadores de eventos tienen un parámetro
extra de evento y retornan <symbol>gint</symbol>. Revisa los
archivos de encabezado de GTK+ cuando necesites verificarlo.
</para>
<para>
Asegúrate que el programa trata de una forma apropiada
todas las acciones generadas por el usuario. Recuerda
que el usuario puede cerrar una ventana en cualquier
momento a través del administrador de ventanas; tenlo
presente y escribe el código necesario para manejar esta
situación.
</para>
<para>
Si verificas los modificadores de teclas con una máscara
de estado de eventos, por ejemplo,
<keycombo><keycap>Control</keycap><keycap>F10</keycap></keycombo>
escribe lo siguiente:
<programlisting>
guint modifiers;
modifiers = gtk_accelerator_get_default_mod_mask ();
if (event->keysym == GDK_F10
&& (event->state & modifiers) == GDK_CONTROL_MASK) {
do_something ();
}</programlisting>
Esto es necesario; si en vez de lo anterior escribes:
<programlisting>
if (event->keysym == GDK_F10 && event->state == GDK_CONTROL_MASK)</programlisting>
entonces el programa no funcionará correctamente si el
usuario tiene, por ejemplo, activada la tecla
<keycap>NumLock</keycap> — <keycap>NumLock</keycap>
también es un modificador, y si está activado, entonces
la máscara de estado del evento no será como se espera
en el segundo ejemplo.
</para>
<!-- Visuals and Colormaps -->
<sect3 id="visuals">
<title>Vistas y mapas de color</title>
<para>
<!-- FIXME: Buscar un término adecuado para "Visual" en
vez de "vista" -->
Algunos usuarios utilizan tarjetas de vídeo avanzadas
(por ejemplo, SGIs y Suns) que soportan múltiples vistas
simúltaneamente. Una vista define la representación en memoria
que usa un dispositivo de hardware para almacenar los
contenidos de una imagen. Muchas tarjetas de vídeo de
PC soportan sólo una vista a la vez, pero el hardware
avanzado puede tener diferentes ventanas y pixmaps en
diferentes vistas simultáneamente.
</para>
<para>
Es importante entender las vistas y mapas de colores si
vas a escribir código que crea ventanas y pixmaps por tus
propios medios, en vez de utilizar las funciones de alto
nivel como GnomeCanvas y GnomePixmap. Para mayor información,
lea el manual de programación de Xlib.
</para>
<para>
En general, sólo necesitas recordar que la vista y el mapa
de colores de un área de dibujo debe coincidir con los de
otra área de dibujo si deseas copiar un trozo desde la
primera área de dibujo a la segunda. Si no son las mismas,
podrías obtener el mensaje «BadMatch error» del servidor X y
tu aplicación muy probablemente abortará su ejecución.
</para>
<para>
Si creas un contexto gráfico (GC) y lo compartes para pintar
en varias áreas de dibujo, asegúrate que todas ellas tengan
la misma vista y mapa de colores que fue definido para el
GC. Lo mismo se aplica si quieres copiar un área desde un
pixmap a una ventana; ambos deben tener la misma vista y mapa
de colores.
</para>
<para>
Si no estás seguro de que tu código lo hace correctamente,
pregunta educadamente, en una de las listas de correo de
desarrollo de GNOME, si alguien puede realizar una prueba con
una tarjeta de video que soporta esta característica. Dicha
persona sabrá como arreglar el problema y te dirá al respecto.
</para>
</sect3>
</sect2>
<!-- Unix-related Issues -->
<sect2 id="unix">
<title>Temas relacionados con Unix</title>
<para>
Verifica los valores de retorno de <emphasis>todas</emphasis>
las llamadas al sistema que el programa realice. Recuerda
que muchas de las llamadas al sistema pueden ser
interrumpidas (por ejemplo, la llamada retorna
<symbol>-1</symbol> y <symbol>errno</symbol> será definido a
<symbol>EINTR</symbol>) y deben reiniciarse.
</para>
<para>
No asumas, por ejemplo, que la función
<function>write(2)</function> escribirá el buffer completo
de una vez; tienes que verificar el valor de retorno, el cual
indica el número de bytes escritos e intenta nuevamente
hasta que sea cero. Si el valor de retorno es
<symbol>-1</symbol>, recuerda
verificar el valor de errno y manejar el error
apropiadamente.
</para>
<para>
Si la aplicación llama a la función <function>fork(2)</function>
sin llamar a <function>execve(2)</function>, recuerda que
el proceso hijo no puede hacer llamadas X. Normalmente se
puede diagnosticar este problema a través de un oscuro
mensaje de error de Xlib.
</para>
<para>
Lee el libro «Advanced programming in the Unix
environment», de Richard Stevens, para aprender acerca de
todos estos teassertmas y asegúrate que tus programas usan
la API de Unix de forma correcta. Si no deseas asegurarte
del uso correcto de las llamadas Unix, pregunta en las
listas de correo.
</para>
</sect2>
</sect1>
<!-- Security Considerations -->
<sect1 id="security">
<title>Consideraciones de seguridad</title>
<para>
La seguridad es un tema complejo y en esta sección no se
puede explicar ni de lejos todo lo relacionado.
Intentaremos indicar las situaciones más comunes donde tus
programas deben interesarse por la seguridad.
</para>
<para>
Es muy fácil crear hoyos de seguridad a través de la creación
incorrecta de archivos temporales en <filename>/tmp</filename>.
Debes garantizar que los archivos que usarás no existen al
momento de su creación. Usar un nombre de archivo «único»
e «impredecible» no es suficiente; debes garantizar que el archivo
con ese nombre no será creado por alguien más entre el tiempo
en que se determina el nombre y el tiempo en que es efectivamente
creado (básicamente los ataques involucran que un tercero
cree un enlace simbólico al archivo que ellos quieren
sobreescribir).
</para>
<para>
Afortunadamente, esto es fácil de hacer. Usa el siguiente trozo
de código:
<programlisting>
char *filename;
int fd;
do {
filename = tempnam (NULL, "foo");
if (!filename) {
fprintf (stderr, "Could not create temporary file name\n");
exit (EXIT_FAILURE);
}
fd = open (filename, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0600);
free (filename);
} while (fd == -1);</programlisting>
Recuerde liberar <symbol>filename</symbol> usando la función
<function>free()</function> y llamar a las funciones
<function>close()</function> y <function>unlink()</function>
con el archivo respectivo cuando haya terminado; aquí
liberamos <symbol>filename</symbol> con <function>free()</function>
inmediatamente y así no causará una pérdida de memoria.
</para>
<para>
Si deseas usar la biblioteca estándar de E/S, puede usar la
función <function>fdopen()</function> para transformar el
descriptor de archivo en <symbol>FILE *</symbol>, o puedes
usar la función <function>tmpfile()</function> para hacerlo
en un solo paso.
</para>
<para>
Intenta no usar buffers de tamaño fijo. Los buffers de tamaño
fijo para cadenas constituyen los típicas fuentes de hoyos
explotables que pueden llegar a oscuros errores. Si definitivamente
debes usar buffers de tamaño fijo para cadenas, usa la función
<function>g_snprintf()</function> para especificar el tamaño
máximo del buffer.
</para>
<para>
Glib proporciona la, muy conveniente, función
<function>g_strdup_printf()</function>, la cual funciona como
<function>sprintf()</function> pero automáticamente
localizará un buffer con el tamaño correcto. El valor de retorno
de esta función debiera ser liberada usando
<function>g_free()</function>. A menudo es más conveniente de
usar que <function>g_snprintf()</function>, ya que esta no
limita el tamaño de las cadenas que un programa puede manipular.
</para>
<para>
Si deseas concatenar un grupo de cadenas, puedes usar la función
<function>g_strconcat()</function>, la cual recibe una lista
variable de cadenas y un puntero a <symbol>NULL</symbol>
como último argumento.
</para>
<para>
<emphasis>Bajo ninguna circunstancia</emphasis> crees un
programa GTK+ con setuid root. Las bibliotecas de GTK+ y
GNOME son grandes y complejas y no han tenido auditorías
de seguridad. En cualquier caso, no debieras querer que
una pieza tan grande código sea setuid root. Si definitivamente
requieres usar privilegios de root para algo, escribe un
programa que sea la interfaz de usuario como un proceso
normal, sin privilegios y crea un programa nexo que
tenga setuid y se encargue de realizar la operaciones
«peligrosas». Además, notifica a las listas
de correo de desarrollo de GNOME indicando que requieres
que alguien realice una auditoría de seguridad a tu
programa nexo.
</para>
<para>
En general, si no estás seguro si puedes crear un riesgo de
seguridad, pregunte en las listas de correo de desarrollo
de GNOME.
</para>
<para>
Puedes leer más sobre temas de seguridad que debieras encontrar
cuando programes una aplicación Unix en el documento
<ulink URL="http://www.fish.com/security/murphy.html">«Murphy's
Law and Computer Security»</ulink>, de Wietse Wenema.
Hay otros documentos de seguridad en <ulink
URL="http://www.fish.com/security">el sitio fish.com</ulink>
que podrías encontrar interesante.
</para>
<para>
Puedes encontrar muchas otras guías útiles para escribir programas
seguros en <ulink
url="http://www.dwheeler.com/secure-programs">«Secure
Programming for Linux and Unix HOWTO»</ulink>.
</para>
</sect1>
<!-- Performace -->
<sect1 id="performance">
<title>Rendimiento</title>
<para>
No leas esta sección hasta que estés seguro que tu programa está
correcto, por ejemplo, cuando estés seguro que funciona
correctamente y no tiene errores. Es más importante que
el código sea correcto a que sea rápido. Un programa lento
pero correcto es mucho mejor a uno rápido pero con errores.
</para>
<para>
Si quieres optimizar tu programa, el primer paso es determinar
un perfil al programa ejecutándolo con datos de la vida real
y recopilar puntos calientes que requieran optimización.
Esto ayudará a determinar los lugares que necesitan optimizarse.
Es importante que nunca adelantes la optimización si no tienes
una idea clara sobre donde está el problema. Podrías terminar
perdiendo el tiempo en agilizar una rutina que no es la causa
del cuello de boteLla y como resultado podrías terminar ofuscando
dicha rutina. Esto podría reducir la legibilidad y mantenibilidad
del código sin ganancia visible en velocidad.
</para>
<para>
Un código sencillo es bueno porque es fácil de entender y
mantener. Si puedes escribir código simple que además sea
potente y rápido, tanto mejor. Si tienes un trozo de código
inteligente pero que no es fácil de seguir, entonces documéntalo
para que las personas no lo estropeen accidentalmente.
</para>
<para>
No escribas código que sea difícil de leer y mantener por el
sólo hecho de hacerlo más rápido. En cambio, prefiere un algoritmo
agradable y claro e impleméntalo claramente.
</para>
<para>
Hacer un buen trabajo en el caso general es a menudo mejor que
tener muchos casos especiales. Sólo provee casos especiales
cuando se han identificado puntos débiles en el programa.
</para>
<!-- List Management in Glib -->
<sect2 id="list">
<title>Administración de listas en Glib</title>
<para>
Evita emplear construcciones que terminen ralentizando
los algoritmos. Si usas
<function>g_list_insert_sorted()</function> o
<function>g_list_append()</function> sin ningún cuidado,
fácilmente puedes tener un algoritmo que se ejecuta en
tiempo proporcional a O(n<superscript>2</superscript>).
Normalmente puedes crear listas hacia atrás, usando
<function>g_list_prepend()</function>, e invirtiéndola
cuando hayas terminado usando
<function>g_list_reverse()</function>. Esta es una
operación O(n). Y si necesitas una lista ordenada, puedes
crearla de la misma forma (hacia atrás) y una vez terminado,
usar <function>g_list_sort()</function>.
</para>
<para>
Si necesitas una lista ordenada en todo momento, puede ser
mejor emplear un estructura de árbol o un híbrido
lista/árbol. Si necesitas construir una lista añadiendo
nodos a ella, mantén un puntero al final de la lista
y ve cambiándola según sea apropiado; esto te permitirá
insertar un nodo al inicio o al final en un tiempo constante.
</para>
</sect2>
</sect1>
<!-- Localization -->
<sect1 id="l10n">
<title>Localización</title>
<para>
Se pretende que GNOME pueda ejecutarse en distintas localidades
y lenguajes, y los programas debieran tener esto en cuenta. No
tienes que localizar los programas, sólo debes permitir que éstos
sean traducibles y localizables.
</para>
<para>
Debes recordar que los diferentes idiomas humanos tienen diferentes
gramáticas, así que no debieras suponer la forma de estructurar
cada frase. Esto es importante, por ejemplo, cuando se
construyen cadenas a partir de trozos separados.
</para>
<para>
La concatenación normalmente no es la forma correcta de construir
una cadena para que sea presentada al usuario. Normalmente terminan
en mensajes que no pueden ser traducidos correctamente en todos
los idiomas. En vez de concatenar, intenta usar
<function>g_strdup_printf()</function>, por ejemplo:
<programlisting>
/* Una forma no muy buena */
char *message = g_strconcat (_("Hello, "),
name,
_(", would you like fries with that?"),
NULL);
/* Una mejor forma */
char *message = g_strdup_printf (_("Hello, %s, would you like fries with that?"),
name);</programlisting>
Esto permitirá al traductor mover %s donde corresponda según
lo requiera la gramática del idioma en el cual trabaja.
</para>
<para>
Recuerda que no todos los idiomas forman los plurales añadiendo
una «s» a las palabras. Además, las estructuras de
frases pueden cambiar con los plurales. Por ejemplo,
<programlisting>
/* Una forma no muy buena */
printf (_("%d happy monkey%s bouncing on the bed."),
num_monkeys,
(num_monkeys > 1) ? "s", "");
/* Una mejor forma */
printf ((num_monkeys > 1)
? _("%d happy monkeys bouncing on the bed."),
: _("%d happy monkey bouncing on the bed."));</programlisting>
Se requiere de esta forma ya que el plural se forma de
distintas maneras en distintos idiomas, y la estructura
completa de la frase puede cambiar.
</para>
<para>
Si el programa muestra fechas u horas, usa la función
<function>strftime()</function> para darles formato como
cadenas. Esto se encargará de usar la representación
adecuada de fecha y hora de acuerdo a la definición local
del usuario. Además, si el programa debe generar una
representación visual de un calendario, recuerda que en
algunos países se considera como primer día de la semana
el domingo y no el lunes. El programa debiera permitir
ambas formas de calendarios.
</para>
<para>
Si el programa usa medidas, asegúrate de permitir tanto
el sistema métrico decimal como el sistema imperial (o anglosajón).
</para>
</sect1>
<!-- Binary Compatibility -->
<sect1 id="binary">
<title>Compatibilidad binaria en las bibliotecas</title>
<para>
Para bibliotecas estables y ya liberadas, es muy importante
intentar preservar la compatibilidad binaria en las revisiones
mayores del código. Los desarrolladores de bibliotecas deben
intentar no alienar a los usuarios realizando de forma frecuente
cambios que sean binariamente incompatibles en bibliotecas que
son de producción. Por supuesto, en aquellas bibliotecas que se
encuentran en desarrollo o están etiquetadas como no finalizadas
puedes realizar tantos cambios como se necesiten.
</para>
<para>
Una biblioteca típicamente exporta un número de interfaces. Estas
incluyen nombres de rutinas, las firmas o prototipos de estas
rutinas, variables globales, estructuras, campos de estructura
(tanto los tipos como su significado), el significado de los
valores enumerados y la semántica de los archivos que la
biblioteca crea. Mantener compatibilidad binaria significa que
estas interfaces no cambiarán. Puedes añadir nuevas interfaces
sin romper la compatibilidad binaria, pero no puedes cambiar
las ya existentes, ya que los programas antiguos no se ejecutarán
correctamente.
</para>
<para>
Esta sección incluye un número de ideas acerca de como lograr
que el código de una biblioteca sea compatible con sus
versiones anteriores.
</para>
<para>
Mantener la compatibilidad binaria significa que los programas
y archivos objetos que fueron compilados con una versión
previa del código continuarán funcionando sin recompilar aún
cuando la biblioteca sea reemplazada. Es posible encontrar
una descripción detallada en el nodo info
<filename>(libtool)Interfaces</filename> de la documentación
de GNU libtool.
</para>
<!-- Private Information in Structures -->
<sect2 id="private">
<title>Información privada en las estructuras</title>
<para>
En el sistema GNOME es una tarea muy común crear un
nuevo <structname>GtkObject</structname> para crear
una estructura cuyo primer miembro corresponde a la
clase de la cual hereda este objeto y posteriormente
un número de variables de instancia que se añaden
después del primer miembro.
</para>
<para>
Este esquema de diseño normalmente lleva a que el código
sea difícil de actualizar y cambiar: si se quiere
extender un objeto donde se necesita mantener su estado
interno, los programadores necesitan recurrir a varios
hacks para mantener el mismo tamaño de las estructuras.
Esto hace difícil agregar nuevos campos a las estructuras
públicas sin romper la compatibilidad binaria, ya que
los campos de la estructura pueden cambiar y el tamaño
de las estructuras pueden variar.
</para>
<para>
Por consiguiente, se sugiere una estrategia que puedan
adoptar los desarrolladores en el momento de crear nuevos
objetos. Esta estrategia asegurará la compatibilidad
binaria y al mismo tiempo mejorará la legibilidad y
mantenibilidad del código.
</para>
<para>
La idea es que, para un objeto dado, el programador
debiera crear tres archivos: el primero que contiene
el contrato entre la biblioteca y el usuario (esto
es el archivo de encabezado que se instala en el
sistema); el segundo contiene la implementación del
objeto; y el tercer archivo contiene la definición
de la estructura para los campos privados o internos
que no necesitan estar en la estructura pública.
</para>
<para>
La API pública de la estructura podría incluir un
puntero a un elemento privado, el cual podría
apuntar a los datos privado de la instancia. Las
rutinas de implementación podría dereferenciar
este punto para acceder a los datos privados; por
supuesto, el puntero apunta a la estructura que se
localiza privadamente.
</para>
<para>
Por ejemplo, imagina que se crea el objeto GnomeFrob.
La implementación del widget será dividido en tres
archivos:
<table>
<title>Archivos que muestran el widget de ejemplo
GnomeFrob</title>
<tgroup cols="2">
<thead>
<row>
<entry>Archivo</entry>
<entry>Contenido</entry>
</row>
</thead>
<tbody>
<row>
<entry><filename>gnome-frob.h</filename></entry>
<entry>API pública de GnomeFrob</entry>
</row>
<row>
<entry><filename>gnome-frob.c</filename></entry>
<entry>Implementación del objeto Gnomefrob</entry>
</row>
<row>
<entry><filename>gnome-frob-private.h</filename></entry>
<entry>Datos privados de la implementación de GnomeFrob.
Debieras querer usar esto si planea compartir los
datos privados a través de varios archivos C. Si
limitas el uso de la información privada a sólo un
archivo, no necesita esto, ya que al definir la
estructura en el archivo de implementación se logrará
el mismo efecto.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
<example>
<title>Ejemplo de la API pública de <filename>gnome-frob.h</filename>
</title>
<programlisting>
typedef struct _GnomeFrob GnomeFrob;
typedef struct _GnomeFrobPrivate GnomeFrobPrivate;
struct _GnomeFrob {
GtkObject parent_object;
int public_value;
GnomeFrobPrivate *priv;
} GnomeFrob;
GnomeFrob *gnome_frob_new (void);
void gnome_frob_frobate (GnomeFrob *frob);</programlisting>
</example>
<example>
<title>Ejemplo de
<filename>gnome-frob-private.h</filename></title>
<programlisting>
typedef struct {
gboolean frobbed;
} GnomeFrobPrivate;</programlisting>
</example>
<example>
<title>Ejemplo de la implementación de
<filename>gnome-frob.c</filename>.</title>
<programlisting>
void gnome_frob_frobate (GnomeFrob *frob)
{
g_return_if_fail (frob != NULL);
g_return_if_fail (GNOME_IS_FROB (frob));
frob->priv->frobbed = TRUE;
}</programlisting>
</example>
Fíjese en el uso de prototipos de estructura: esto permite que
el compilador realice más verificaciones en tiempo de
compilación respecto a los tipos que estan siendo empleados.
</para>
<para>
Este esquema es útil en algunas situaciones, particularmente
en el caso en el cual vale la pena almacenar un puntero
extra para una instancia del objeto. Si esto no fuera
posible, el programador necesitaría recurrir a otros
artilugios para evitar este problema.
</para>
<para>
El propósito de tener un archivo de encabezado privado
extra para las estructuras privadas es permitir que las
clases derivadas usen esta información. Por supuesto,
una buena práctica de programación indicaría que debiera
proveerse de interfaces o métodos de acceso precisamente
para que el programador no tenga que lidiar con
estructuras privadas. Se debiera intentar alcanzar un
balance entre buena práctica y pragmatismo cuando se
crean estructuras privadas y métodos de acceso públicos.
</para>
<para>
Podrían aparecer algunos problemas, por ejemplo, cuando
se ha enviado un versión del código y que está siendo
ampliamente usado y se desea extenderlo. Hay dos
soluciones para esto.
</para>
<para>
La primera opción es encontrar un campo puntero en la
estructura pública que pueda hacerse privada y
reemplazar el puntero a la parte privada de la
estructura. Esto preserva el tamaño de la estructura
ya que, para los propósitos de GNOME, se puede
asumir que los punteros son todos del mismo tamaño.
Se puede hacer que este campo apunte a la parte privada
de la estructura que será ubicada por la función que
originalmente creaba la estructura pública. Es
importante que este campo sea llamado
<structfield>private</structfield>, se trata de
una palabra reservada de C++ y creará problemas cuando
el archivo de encabezado sea usado desde programas
fuentes C++ — mejor llámalo
<structfield>priv</structfield>. Por supuesto, este
tipo de cambios sólo funcionará si el antiguo campo
puntero solamente era usado para propósitos internos;
si los usuarios de la biblioteca habían tenido acceso
a dicho campo para cualquier propósito, será necesario
buscar otro campo o buscar una solución diferente.
</para>
<para>
Si la estructura original fue derivada de
<structname>GtkObject</structname> y no hay campos punteros
que puedan ser reemplazados, puedes usar la facilidad
de datos de objetos de GTK+. Esto permite asociar punteros
a datos arbitrarios a objetos. Usa la función
<function>gtk_object_set_data()</function> para adjuntar
valores o estructuras a un objeto y cuando requieras usarlos
la función <function>gtk_object_get_data()</function>
permitirá recuperar dichos valores. Esto puede ser empleado
para adjuntar una estructura privada a un objeto GTK+.
Esto no es tan eficiente como el enfoque anterior, pero podría
no importar para el dominio particular de la aplicación. Si
los datos serán accedidos frecuentemente, se pueden usar
quarks en vez de cadenas para agregar y obtenerlos a través
de las funciones
<function>gtk_object_set_data_by_id()</function> y
<function>gtk_object_get_data_by_id()</function>,
respectivamente.
</para>
</sect2>
</sect1>
<!-- Modifying Other People's Code -->
<sect1 id="modify">
<title>Cómo modificar el código de otros</title>
<para>
GNOME es un proyecto de equipo, así las contribuciones de
código a programas de otras personas siempre son apreciadas.
Sigue los siguientes lineamientos cuando modifiques el código
de otra persona.
</para>
<!-- General Etiquette -->
<sect2 id="etiquette">
<title>Etiqueta general</title>
<para>
Siga el mismo estilo de indentación usado en el código
original. El código original perdurará durante más tiempo
que el que le pueda dedicar, mantener las
contribuciones consistentes respecto a la indentación
es más importante que forzar su estilo de indentación
en el código.
</para>
<para>
Aunque sus parches pueden implementar una
funcionalidad muy llamativa, es muy molesto para el
autor tener que reindentar el código antes de aplicar
un parche al código principal. Así, si el código
original luce como:
<programlisting>
int
sum_plus_square_of_indices (int *values, int nvalues)
{
int i, total;
total = 0;
for (i = 0; i < nvalues; i++)
total += values[i] + i * i;
return total;
}</programlisting>
entonces no agregues una función que luce como:
<programlisting>
int sum_plus_cube_of_indices(int *values, int nvalues) {
int i,total;
total=0;
for (i=0;i<nvalues;i++)
total+=values[i]+i*i*i;
return total;
}</programlisting>
En el segundo ejemplo, la indentanción y ubicación de las
llaves no coincide con el código original, no hay espacios
alrededor de los operadores y el código no se parecerá
al original. Sigue el estilo de programación del autor
original del programa y los parches que envías tendrán una mejor
oportunidad de ser aceptados.
</para>
<para>
No repares errores con artilugios o hacks rápidos; repáralo
correctamente. Tampoco añadas características como hacks o
con código que no sea extensible; es mejor rehacer el
código original para que sea extensible y luego agrega
la nueva característica, utilizando el código como
estructura.
</para>
<para>
La limpieza de código siempre es bienvenida; si encuentras
trozos de código feos y sucios en GNOME, será muy
apreciado si envías parches que hagan el código más
agradable y fácil de mantener.
</para>
<para>
Como siempre, asegúrate de que el código que contribuye compila
sin ningún tipo de aviso, que tenga los prototipos correctos
y que sigan las directrices de este documento.
</para>
</sect2>
<!-- Documenting Your Changes -->
<sect2 id="document">
<title>Cómo documentar los cambios</title>
<para>
GNOME utiliza los archivos &ChangeLog; estándar de GNU para
documentar los cambios al código. Cada cambio que
efectúes a un programa <emphasis>debe</emphasis> ser
acompañado por una registro en el &ChangeLog;. Esto
permite a las personas leer la historia de cambios
del programa de una manera sencilla.
</para>
<para>
Si usas &Emacs;, puede agregar las líneas al
&ChangeLog; presionando «C-x 4 a».
</para>
<note>
<para>
Si conoces la forma de realizarlos para otros editores
populares, agradeceremos que nos lo hagas saber para
expandir este documento.
</para>
</note>
<para>
Cada registro en el &ChangeLog; tiene un forma general:
<programlisting>
1999-04-19 J. Random Hacker <jrandom@foo.org>
* foo.c (some_function): Changed the way MetaThingies are
frobnicated. We now use a hash table instead of a linked list to
look MetaThingies up.
(some_other_function): Support the MetaThingies hash table by
feeding it when necessary
* bar.c (another_function): Fixed bug where it would print "Take
me to your leader" instead of "Hello, World".
1999-04-18 Johnny Grep <grep@foobar.com>
* baz.c (ugly_function): Beautified by using a helper function.</programlisting>
</para>
<para>
Si agregas una nueva función a una biblioteca, escribe
la referencia necesaria y la documentación de programación.
Puedes escribir una referencia a la documentacion
en línea usando el formato de comentarios descrito en
<filename>gnome-libs/devel-docs/api-comment-style.txt</filename>.
Si usas &Emacs;,
<filename>gnome-libs/tools/gnome-doc/gnome-doc.el</filename>
provee un acelerador que puede ser empleado para añadir
una plantilla de documentación al programa.
</para>
</sect2>
<!-- Changing Code on CVS -->
<sect2 id="change-cvs">
<title>Cómo actualizar en CVS</title>
<para>
Si tienes acceso de escritura en el repositorio CVS de GNOME,
debes seguir algunas políticas adicionales. Ya que estás
trabajando en la copia maestra de los fuentes, debes tener
especial cuidado.
</para>
<para>
Si estás reparando algo en un programa que está en el CVS
o si estás añadiendo una funcionalidad allí, y si no has
estado trabajando en dicho programa por un período largo
de tiempo, pregunta al autor original antes de aplicar
sus parches. Generalmente está bien formular estas
preguntas en la lista de correo
<filename>gnome-devel-list</filename>.
</para>
<para>
Una vez que el autor del programa te indique como un
‘contribuyente frecuente’, puedes comenzar
aplicar los parches sin previo consentimiento. Si
quieres aplicar una reorganización mayor del código,
debieras preguntar primero.
</para>
<para>
Algunos módulos en el CVS tienen rama estable y de
desarrollo. Normalmente el desarrollo debiera ir
en la rama HEAD en el CVS, y la rama estable debiera
mantenerse separadamente. Generalmente no se añaden
nuevas características a las rama estable; sólo
se reparan errores. Repara el error a la rama estable
y pregunta al autor principal sobre la política para
mezclar parches en la rama de desarrollo; algunos
autores prefieren hacerlo por lotes, mientras hay otros
que prefieren mezclarlos inmediatamente.
</para>
<para>
Si trabajas en una característica experimental que
podría estropear mucho código, crea una rama en el CVS
y realiza los cambios allí. No los mezcles en la rama
principal de desarrollo hasta que te encuentres
razonablemente seguro que funcionará correctamente y que
se integrará bien dentro del resto del código. El uso de
una rama para trabajo en características experimentales
te permitirá evitar interrumpir el trabajo de otros
desarrolladores. Pregunte al autor principal antes de
mezclar tu rama y traer sus cambios a la parte principal
del árbol.
</para>
<para>
Como siempre, agrega un registro en el &ChangeLog;
cuando realices un cambio. Algunos autores tienen la
política de rechazar, correctamente, los cambios que
no tienen tal registro.
</para>
<para>
Algunas veces existen diferentes políticas para los módulos,
verifica si el módulo contiene un archivo
<filename>README.CVS</filename>. Si es así, lee ese archivo
antes de realizar cambios.
</para>
<!-- Branches and Tags -->
<sect3 id="branches">
<title>Ramas y marcas</title>
<para>
Tenemos una convención para nombrar las ramas y marcas o
puntos de una rama en el repositorio de CVS de GNOME.
Las marcas deben ser definidas después que se libera cada
nueva version y deben ser de la forma
«MODULE_MAJOR_MINOR_MICRO», por ejemplo,
«GNOME_LIBS_1_0_53». Los puntos de una rama
deben ser de la forma «MODULE_BRANCH_ANCHOR»,
como en «GNOME_LIBS_1_0_ANCHOR». Finalmente,
una rama raíz en este punto debiera ser de la forma
«module-branch», por ejemplo,
«gnome-libs-1-0». Usa esta convención cuando
crees marcas, puntos de una rama y ramas.
</para>
</sect3>
<!-- Additional CVS Policies -->
<sect3 id="cvs-policies">
<title>Políticas adicionales del CVS</title>
<para>
CVS no proporciona un manera preconstruida para renombrar archivos
o moverlos a otros directorios. Debiera planificar
cuidadosamente el árbol del repositorio de tal forma que evite
mover o renombrar archivos.
</para>
<para>
En el caso que debas mover o renombrar un archivo en el CVS,
<emphasis>NO EJECUTES</emphasis> «cvs remove»
y luego «cvs add». Si lo haces, los archivos
«nuevos» perderán la capacidad de seguir su
historial de cambios, ya que desde el punto de vista de CVS
serán archivos completamente nuevos en su revisión
inicial. Un objetivo del repositorio CVS de GNOME es
poder seguir la historia de los fuentes de manera
precisa desde el inicio.
</para>
<para>
<emphasis>Por favor</emphasis>, pregunte al mantenedor
del CVS, esto es, una persona conocida con acceso shell
a la máquina con CVS, para realizar la cirugía necesaria
para mover los archivos por ti. Esto requiere
conocimiento de la forma en que funciona CVS y debe ser
hecho muy cuidadosamente. La recuperación de un error de
«cvs add/remove» es una tarea muy desagradable;
Si mueves archivo en esta forma equivocada, un mantenedor
del CVS, quien tendrá que ir a reparar y realizar el trabajo
sucio, reclamará las penas del infierno. Así es que mejor
pregunta por una persona que pueda mover los archivos por
ti.
</para>
</sect3>
</sect2>
</sect1>
<!-- Maintaining a Package -->
<sect1 id="maintain">
<title>Cómo mantener un paquete</title>
<para>
Un mantenedor de paquete es una persona que se preocupa de
liberar versiones, integrar cambios de otras personas y,
en general, ser responsable de un paquete. Durante el
período de vida de un paquete, éste puede cambiar de
mantenedores, por ejemplo, una persona que pierde interés
o que ya no puede dedicarle tiempo suficiente como mantenedor.
</para>
<para>
En conformidad con los Estándares de programación de GNU, GNOME
usa GNU Autoconf para manejar la portabilidad y GNU Automake
para crear makefiles. Automake hace especialmente fácil
construir paquetes correctos, así que si eres un mantenedor
de paquetes, debieras aprender como usarlo. Autoconf y
Automake tienen muchas áreas truculentas, así que siéntete
libre de pedir ayuda en las listas de desarrollo de GNOME
si tienes preguntas sobre la forma correcta de construir
makefiles para tu paquete.
</para>
<para>
Un número de personas regularmente contribuye con traducciones
localizadas de los catálogos de mensajes en los paquetes de
GNOME. Para mantener las traducciones actualizadas tanto como
sea posible, los mantenedores deben coordinarse con los
traductores para que estos últimos puedan actualizar las
traducciones a tiempo cuando se va a liberar una nueva
versión del paquete. Una buena forma de notificar a los
traductores consiste en enviar un mensaje a
<email>gnome-i18n@gnome.org</email> con suficientes
días de anticipación. Esto les permitirá actualizar sus
respectivas traducciones a tiempo para puedan ser incluidas
en la siguiente versión liberada.
</para>
</sect1>
<!-- Memory Leaks Agenda -->
<sect1 id="memory-leak">
<title>¿Por qué preocuparse por la pérdida de memoria?</title>
<para>
Échale un vistazo a la lista de errores de Mozilla y una cosa es
clara: el nuevo código que entra a Mozilla no es (o no ha sido)
verificado suficientemente por pérdida de memoria y problemas
de acceso.
</para>
<para>
Las pérdidas de memoria son malas por varias razones:
<itemizedlist>
<listitem>
<para>
Se comerá el área de intercambio lentamente. A la larga tu
programa, o hasta tu máquina sucumbirán.
</para>
<para>
Nadie quiere que su programa o biblioteca adquiera la
reputación de ser una porquería sólo porque uno ha sido un
vago. Déjaselo a las personas en Redmond.
</para>
</listitem>
<listitem>
<para>
Ocultan el mal uso de la memoria. Si olvidas liberar la
memoria, no hay forma de atrapar las lecturas y
escrituras a esos trozos de memoria. Si, posteriormente,
reparas la pérdida, una parte completamente distinta
del programa podría fallar.
</para>
</listitem>
<listitem>
<para>
Cuando se usan verificadores automáticos de memoria,
nadie quiere ver 600 problemas, de los cuales 500
se encuentran en las bibliotecas de soporte. Lo que
deseas saber es que estaba todo limpio antes que comenzara
el caos, y por lo tanto, esos problemas te corresponde a ti
arreglarlos.
</para>
<para>
Este es uno de los problemas de Mozilla actualmente:
pierde memoria sin parar así que ¿cómo vas a saber si has
contribuido al problema?
</para>
</listitem>
</itemizedlist>
</para>
<sect2 id="memory-advice">
<title>Algunos consejos contra la pérdida de memoria.</title>
<para>
<itemizedlist>
<listitem>
<para>
Usa «<symbol>const</symbol>» donde sea posible (para
punteros obviamente).
</para>
<para>
Si un argumento es «<symbol>const</symbol>», entonces
indudablemente la función llamada no liberará la memoria por
ti.
</para>
<para>
Si el tipo de resultado de una función es
«<symbol>const</symbol>», entonces
claramente no eres responsable por liberarlo.
<emphasis>Probablemente</emphasis> lo será para un resultado
distinto de «<symbol>const</symbol>» (que no sea un entero
o algo similar).
</para>
<para>
Fíjate en que, desafortunadamente, esto no funciona muy bien
con los objetos que cuentan referencias. Funciona bien
con cadenas.
</para>
<para>
Dado que C usa llamadas por valor, no tiene sentido
aplicar «<symbol>const</symbol>» a tipos
<symbol>int</symbol>, <symbol>double</symbol> y similares.
</para>
</listitem>
<listitem>
<para>
<emphasis>Documenta las responsabilidades.</emphasis>
</para>
<itemizedlist>
<listitem>
<para>
Si una función toma la propiedad de un objeto/referencia,
sé explícito acerca de ello.
</para>
</listitem>
<listitem>
<para>
Indica siempre si el que llama a la función debiera
liberar/desreferenciar los resultados.
</para>
</listitem>
<listitem>
<para>
Documenta <emphasis>cómo</emphasis> entregar la
memoria: unref, free, g_free, ...
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
<emphasis>Se cuidadoso cuando uses copiar-y-pegar.</emphasis>
</para>
<para>
El proceso de cortado y pegado no mejora el código, al
contrario de lo que creen muchos programadores. Primero,
mira el código y si intenta producir muchas copias, quizás
necesite una función ayudante o nexo. <!-- FIXME: mejor
traducción de helper -->
</para>
</listitem>
<listitem>
<para>
<emphasis>Libera todo antes de salir.</emphasis>
</para>
<para>
Esto lleva tiempo, así que quizás debieras hacerlo
sólo dentro de una condición.
</para>
<para>
El motivo de este consejo es que los verificadores de memoria
toman tiempo en determinar entre una pérdida que tu conoces
y no te preocupan, y las otras pérdidas.
</para>
</listitem>
<listitem>
<para>
<emphasis>No dejes punteros sueltos en tus estructuras de
datos.</emphasis>
</para>
<para>
Asigna <symbol>NULL</symbol> o
<symbol>(void *)0xdeadbeef</symbol> para los miembros
liberados a menos que vayas a liberar la estructura a la
que pertenecen.
</para>
<para>
Los punteros sueltos tienen la mala tendencia a ocultar pérdidas
de memoria.
</para>
</listitem>
<listitem>
<para>
<emphasis>Ejecuta el nuevo código en un ciclo 1 millón de
veces.</emphasis>
</para>
<para>
Si pierdes memoria, lo sabrás — sólo sigue al proceso con
<command>top</command> en otra ventana. Podrías querer
especificar a top el <symbol>PID</symbol>
del proceso con la opción <parameter>-p</parameter>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Repáralo ahora, no después.</emphasis>
</para>
<para>
No escribas una porquería de código; más temprano que
tarde te pesará.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
</article>