55.4. Convenciones de programación diversas #

Estándar C #

El código en PostgreSQL solo debe depender de las características del lenguaje disponibles en el estándar C99. Esto significa que un compilador conforme a C99 tiene que ser capaz de compilar postgres, al menos aparte de unas pocas piezas que dependen de la plataforma.

Actualmente, no se permite utilizar algunas características incluidas en el estándar C99 en el código principal de PostgreSQL. Esto incluye actualmente los arrays de longitud variable, las declaraciones mezcladas con código, los comentarios de tipo // y los nombres de caracteres universales. Las razones para ello incluyen la portabilidad y las prácticas históricas.

Se pueden utilizar características de revisiones posteriores del estándar C o características específicas del compilador, siempre que se proporcione una alternativa (fallback).

Por ejemplo, actualmente se utilizan _Static_assert() y __builtin_constant_p, a pesar de que pertenecen a revisiones más nuevas del estándar C y a una extensión de GCC respectivamente. Si no están disponibles, recurrimos respectivamente al uso de un reemplazo compatible con C99 que realiza las mismas comprobaciones pero emite mensajes bastante crípticos, y no utilizamos __builtin_constant_p.

Macros de tipo función y funciones en línea (inline) #

Se pueden utilizar tanto macros con argumentos como funciones static inline. Estas últimas son preferibles si existen riesgos de evaluación múltiple cuando se escriben como macro, como ocurre, por ejemplo, con:

#define Max(x, y)       ((x) > (y) ? (x) : (y))

o cuando la macro sería muy larga. En otros casos, solo es posible utilizar macros, o al menos resulta más fácil. Por ejemplo, porque es necesario pasar expresiones de varios tipos a la macro.

Cuando la definición de una función en línea hace referencia a símbolos (es decir, variables, funciones) que solo están disponibles como parte del backend, es posible que la función no sea visible cuando se incluye desde el código del frontend.

#ifndef FRONTEND
static inline MemoryContext
MemoryContextSwitchTo(MemoryContext context)
{
    MemoryContext old = CurrentMemoryContext;

    CurrentMemoryContext = context;
    return old;
}
#endif   /* FRONTEND */

En este ejemplo se hace referencia a CurrentMemoryContext, que solo está disponible en el backend, y la función queda oculta con un #ifndef FRONTEND. Esta regla existe porque algunos compiladores emiten referencias a símbolos contenidos en funciones en línea incluso si la función no se utiliza.

Escribir manejadores de señales #

Para ser adecuado para ejecutarse dentro de un manejador de señales, el código debe escribirse con mucho cuidado. El problema fundamental es que, a menos que esté bloqueado, un manejador de señales puede interrumpir el código en cualquier momento. Si el código dentro del manejador de señales utiliza el mismo estado que el código exterior, puede desatarse el caos. Como ejemplo, considera qué sucede si un manejador de señales intenta adquirir un bloqueo que ya se tiene en el código interrumpido.

Salvo disposiciones especiales, el código en los manejadores de señales solo puede llamar a funciones seguras para señales asíncronas (async-signal safe, según se define en POSIX) y acceder a variables de tipo volatile sig_atomic_t. También se consideran seguras para señales unas pocas funciones en postgres, de manera importante SetLatch().

En la mayoría de los casos, los manejadores de señales no deberían hacer nada más que registrar que ha llegado una señal y despertar el código que se ejecuta fuera del manejador utilizando un latch. Un ejemplo de este tipo de manejador es el siguiente:

static void
handle_sighup(SIGNAL_ARGS)
{
    got_SIGHUP = true;
    SetLatch(MyLatch);
}

Llamar a punteros de función #

Para mayor claridad, se prefiere desreferenciar explícitamente un puntero de función al llamar a la función a la que apunta si el puntero es una variable simple, por ejemplo:

(*emit_log_hook) (edata);

(aunque emit_log_hook(edata) también funcionaría). Cuando el puntero de función forma parte de una estructura, entonces la puntuación adicional se puede y normalmente se debe omitir, por ejemplo:

paramInfo->paramFetch(paramInfo, paramId);