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()
です。
ほとんどの場合、シグナルハンドラはシグナルが到着したことを記録し、ハンドラの外部で動作しているコードをラッチを使って呼び起こす以上のことをすべきではありません。 以下はそのようなハンドラの例です。
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);