54.4. 其他编码习惯

C 标准

PostgreSQL中的代码应该只依赖于 C99 标准中的语言特性。这意味着遵循 C99 的编译器一定能编译 postgres,至少除开少数平台依赖问题之外。

目前,C99 标准中包含的一些特性,不允许用于核心功能 PostgreSQL代码。 这当前包括可变长度数组、混合声明和代码、// 注释、通用字符名称。原因包括可移植性和历史实践。

如果提供了回退,则可以使用 C 标准或编译器后续版本中的特性。

例如当前使用的 _Static_assert()__builtin_constant_p , 即使它们分别来自 C 标准的更新修订和 GCC扩展。 如果没有,我们分别回退到 C99 兼容替换来执行相同的检查,但会发出相当神秘的消息,并且不使用__builtin_constant_p

类函数的宏以及内联函数

带有参数的宏以及static inline函数都可能被使用。当类似如下情况写作宏时会有多次计算风险或者宏可能非常长时,使用后者会更好。

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

在其他情况下只能使用宏,或者说使用宏至少更容易。例如,由于多种类型的表达式需要被传递给宏。

当一个内联函数的定义引用只在后端中可用的符号(即变量、函数)时,从前端代码引用该函数时该函数可能不可见。

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

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

在这个例子中,CurrentMemoryContext只在后端中可用,但该函数引用了它并且该函数因此被#ifndef FRONTEND隐藏。之所以存在这条规则,是因为即使内联函数中包含的符号没有被使用,有些编译器也会发出对它们的引用。

编写信号处理器

为了能适合在信号处理器中运行,代码必须被非常仔细地编写。根本问题是,除非被阻塞,信号处理器能在任何时候打断代码。如果信号处理器内部的代码使用和外面代码相同的状态,很可能会出现混乱。例如,可以想想如果一个信号处理器试图取得已经在被打断代码中持有的锁时会发生什么。

除特殊安排的代码之外,在信号处理器中只应该调用对异步信号安全的函数(如 POSIX 中定义的那样)并且只访问volatile sig_atomic_t类型的变量。一些postgres中的函数也被视作是信号安全的,其中很重要的一个是SetLatch()

在大部分情况下,信号处理器应该只提示一个信号已经到达,并且使用一个 latch 唤醒运行在处理器之外的代码。这样一个处理器的例子如下:

static void
handle_sighup(SIGNAL_ARGS)
{
    int         save_errno = errno;

    got_SIGHUP = true;
    SetLatch(MyLatch);

    errno = save_errno;
}

errno会被保存并且恢复,因为SetLatch()可能会更改它。如果不这样做,当前正在观测errno的被中断代码可能会看到错误的值。

调用函数指针

为了清晰,如果函数指针是一个简单的变量,在调用指向的函数时显式地对其解除引用会更好。例如:

(*emit_log_hook) (edata);

(虽然emit_log_hook(edata)还会有效)。当函数指针是一个结构的组成部分时,则额外的标点符号能够被省略并且通常也应该被省略,例如:

paramInfo->paramFetch(paramInfo, paramId);