さて、準備ばかりでも退屈なので、そろそろ実際の実装に入っていきたいと思います。といっても、最初は、標準Cライブラリの中でも最もくだらない<iso646.h>です(準備以上に退屈かもしれません)。こういうくだらないものは最初に片付けておいて、楽しみは後に取っておくことにしましょう。
<iso646.h>は、ISO-646のように、C言語の基本ソース文字集合として必要な文字を欠いている文字コードを使わざるを得ない処理系のための代替表現マクロを定義するものです。言葉にすると、何か難しいのですが、要するにこのヘッダを使うことは実際にはまずありません。
用途としては、このヘッダは全くの役立たずですので、今回は<iso646.h>を題材として、ヘッダを記述する上での作法を解説したいと思います。
まず、最初にお断りしておかなければならないのですが、C言語の標準規格では、標準ヘッダはファイルであるとは限りません。#include指令を使ったときに、必要な宣言や定義がなされたものとして扱うだけで、実際にはファイルを取り込まないような実装でも構わないわけです。
しかし、そのような処理系では、標準ライブラリを自作することができません。gccをはじめ、ほとんどの処理系ではヘッダはファイルとして実現されています。今回の実装でも、それを前提として進めていきたいと思います。
まず、ヘッダを記述する際には、そのヘッダを複数回インクルードされても、一度しか内容が取り込まれないようにするために、二重インクルードガードを施します。具体的には次のようにします。
#ifndef _ISO646_H #define _ISO646_H
/* ヘッダの内容 */
#endif
これは、最初にインクルードされたときには、まだ_ISO646_Hマクロが定義されていませんから、ヘッダの内容はそのまま反映されます。しかし、初回のインクルード時に、2行目の#define指令で_ISO646_Hマクロが定義されますので、再びこのヘッダをインクルードしようとしても、1行目の#ifndef指令によって除去されます。
ここで注意点が二つあります。
まずは、_ISO646_Hというマクロ名です。このマクロ名を具体的にどんな名前にするかという決まりはありませんが、下線(アンダースコア)で始まり、大文字が続いていることが重要なのです。あるいは二重の下線から始まってもかまいませんが、二重の下線で始まる名前はC++コンパイラが内部的に使っている場合があるので、避けた方が無難です。
下線で始まり、下線または大文字が続く識別子は「予約済み識別子」と呼ばれます。予約済み識別子は、コンパイラと標準ライブラリに予約されているため、ユーザーが定義することはできません(定義した場合の動作は未定義です)。
今回は、標準ライブラリの実装ですから、規格で定められた識別子以外はユーザーに開放しなければなりません。したがって、通常とは逆に、ライブラリが内部的に使用する識別子は、全て予約済み識別子でなければなりません。
もう一つの注意点は、ファイルの末尾にあります。#endifの後ですが、ここは必ず改行文字がなければなりません。空でないソースファイルが改行文字で終わらなかった場合の動作は未定義だからです。
未定義の動作というのは、何も実行時だけのものではありません。コンパイル時の動作にも関わるのです。すなわち、コンパイルエラーが発生するならまだよいのですが、何事もなかったかのようにコンパイルが完了し、でたらめなコードを生成していても、文句は言えないのです。
▽続きを読む▽
今回、お話すべき内容はもう終わりなのですが、一応、ヘッダの中身についても触れておきましょう。 <iso646.h>の中で定義されるマクロは、C++では予約語として扱われます。したがって、ヘッダをC++にも対応させるには、 #ifndef __cplusplus
/* 内容 */
#endif
としておかなければなりません。つまり、C++では<iso646.h>は何一つ実体がないのです。 いよいよヘッダの中身ですが、次のように演算子に対応するマクロが定義されます。 #define and && #define and_eq &= #define bitand & #define bitor | #define compl ~ #define not ! #define not_eq != #define or || #define or_eq |= #define xor ^ #define xor_eq ^=
といった具合です。マクロの意味は見ての通りで、特に説明は要らないでしょう。
|