Перейти к основному содержимому

Различные соглашения по оформлению кода

Стандарт C

Код PostgreSQL должен опираться только на возможности языка, доступные в стандарте C99. Это означает, что компилятор, соответствующий стандарту C99, должен уметь компилировать Postgres, по крайней мере, за исключением нескольких частей, зависящих от платформы.

Некоторые возможности, включенные в стандарт C99, на данный момент не разрешены к использованию в основном коде Post- greSQL. В настоящее время это массивы переменной длины, перемежающиеся декларации и код, // комментарии, универсальные имена символов. Причины этого - переносимость и историческая практика.

Возможности из более поздних версий стандарта 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().

В большинстве случаев обработчики сигналов не должны делать ничего, кроме как отмечать, что сигнал пришел, и пробуждать код, выполняющийся вне обработчика, с помощью защелки. Примером такого обработчика может служить следующее:

static void handle_sighup(SIGNAL_ARGS)

{

intsave_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);