| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
このノードでは, プログラムを設計する際に考慮すべき点に ついて幾つか議論します.
3.1 言語は何を使うべきか 3.2 他の実装との互換性 3.3 標準以外の機能を使うには 3.4 標準 C と 標準以前の C 標準 C の機能を使うときの注意点 3.5 条件付コンパイル
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
コンパイル言語で高速に実行される言語を使いたいのなら,一番良いのは C 言語を使うことです. 他の言語を使うのは標準外の機能を使うようなものです. ユーザにとってはトラブルの元です. もし, GCC が C 以外の言語を サポートしていたとしても, プログラムを構築するには, その言語用に GCC を インストールしなければならないとしたら, 面倒だと感じるでしょう. 例えば, 読者が C++ でプログラムを書いたとすると, 他の人は それをコンパイルするためには GNU C++ コンパイラをインストールしなくては なりません.
C++ やその他のコンパイル言語に比べて C には一つ有利な点があります. C を知っている人の方が多いので, C で書かれたプログラムを読んだり 修正したりするのが簡単にできる人の方が多いということになります.
このため,他の候補となる言語よりも C を使った方が一般には ずっと良いのです.
ただし,例外が二つあります.
拡張性を持つように設計されているプログラムはたくさんある. そういうプログラムは C 言語よりも高級な言語のインタプリタを 内蔵している.そのプログラム自体もその言語で書かれていることも多い. Emacs エディタがこの技法の先駆けとなった.
GNU ソフトウェアの標準の拡張用インタプリタは GUILE である.GUILE は Scheme 言語の実装である(Scheme は Lisp の非常にきれいで簡潔な方言である). http://www.gnu.org/software/guile/. われわれは他の「スクリプト言語」 例えば Perl や Python で書かれたプログラムを拒絶することはないが, GUILE を使うことは GNU システム全体の一貫性のためには非常に重要である.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
特別な例外を除いて, GNU のユーティリティプログラムとライブラリは バークレー版 Unix のものと上位互換性がなければなりません. また, 標準 C で仕様が規定されているものについては 標準 C との 上位互換性が, また, POSIX で仕様が規定されているものについては POSIXとの上位互換性が なければなりません.
これらの標準の間で相反するものがあるなら, 標準ごとに互換モードを提供するのが 良いでしょう.
標準 C や POSIX が拡張を禁じている事項はいろいろありますが, とりあえず, あまり気にせず拡張しておいて, `--ansi' や `--posix', `--compatible' などの オプションを指定すると拡張機能が動作しないようにしましょう. しかし, そういう拡張によって, 既存のプログラムやスクリプトが動かなくなる 可能性があるなら, それは本当の上位互換とは言えません. 上位互換性を持つようにインターフェースを再設計すべきでしょう.
GNU のプログラムの多くは,環境変数 POSIXLY_CORRECT が定義されていれば
(その値がヌルでも), POSIX 規格に反する拡張機能を抑止します.
自作のプログラムにそのような拡張機能があるなら,
是非, この環境変数を認識するようにしてください.
(プログラムやコマンドファイルの中から使われることが無く)
ユーザだけが直接その機能を使っているような機能で,
しかも Unix で対応するものは機能が貧弱である, という場合には,
全く異なったより良いものに気楽に置き換えてしまいましょう.
(たとえば, vi は Emacs に置き換えられます.)
しかし, 互換性のある機能を提供することも同様に歓迎します.
(フリーの vi クローンがあり, 我々はそれを提供しています.)
新たな有用な機能は,それに先行するものがあるかどうかに関わらず 歓迎します.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
既に存在する多くの GNU の機能は, 類似のUnix の機能と比べて便利な拡張を多く サポートしています. 読者が自分のプログラムを実装するときに, これらの拡張を使うかどうかは難しい問題です.
ある面でいうと,拡張機能を使うとプログラムがきれいに書けます. その一方で, 他の GNU ツールがないとプログラムを構築できなくなり, プログラムが動く計算機が少なくなってしまうという事態を引き起こします.
拡張機能によっては, 両方の選択肢を提供するのが容易な場合もあるでしょう.
たとえばキーワード INLINE をつけて関数を定義しておき,
INLINEは,コンパイラによって inline または空に展開される
マクロとして定義しておくことができます.
一般的に, 拡張機能を使わずとも素直に書くことができるなら, それが 一番良いでしょう. しかし, 拡張機能を使うことによって得られる改善が非常に大きいものであれば どんどん使いましょう.
プログラムが大きくて完成されたものであり,いろんな種類の計算機で動いている ものなら( Emacs のように)それはこの規則の例外です. このようなプログラムは GNU の拡張を使うと多くのユーザを不幸に陥れることになるので,我々は 使わないようにしています.
コンパイル過程の一部として使われるプログラム, つまり GNU のコンパイル機能を ブートストラップするために, 他のコンパイラでコンパイルする必要のある プログラムも例外です. もしそれらが GNU コンパイラを必要とするようなら, GNU コンパイラを インストールする前には, 誰もそのプログラムをコンパイルできません. これはある場合に非常に問題になります.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
標準 C 1989 は今や充分普及しているので,新しいプログラムで その機能を使っても問題はありません. ただし,ひとつだけ例外が あります. 標準 C のトリグラフの機能は絶対使わないようにしましょう.
標準 C 1999 はまだ広まっていないので,プログラムの中でこの標準の機能を 使わないようにしてください.既にある機能を使うのはかまいません.
とは言っても,大体のプログラムでは標準以前のコンパイラをサポート するのは簡単な事が多いので, やり方を知っているならサポートしても 良いでしょう. もし読者が保守しているプログラムが非 ANSI コンパイラをサポート しているなら, それが動作するように維持してください.
標準以前の C をサポートするには, 関数の定義を書く場合に, 標準のプロトタイプ形式
int foo (int x, int y) ... |
の代わりに, 次のように標準以前の形式で書きましょう.
int
foo (x, y)
int x, y;
...
|
そして, これとは別に宣言を書いて引数のプロトタイプを指定します.
int foo (int, int); |
いずれにせよ,このようなプロトタイプ宣言は必要で,ヘッダファイルに 宣言を入れておけば,その関数を呼び出している全てのファイルで プロトタイプの御利益が得られます. そして, 一度宣言を書いておけば, 関数の定義を標準以前の形式で 書いても失うものは何もありません.
この方法は, int よりも小さい整数型には使えません.
ある引数の型を int よりも小さい型にしたいと思ったら,
変わりに int として宣言しましょう.
この方法を使うのが難しいような, 特別な場合が幾つかあります.
例えば, ある関数の引数がシステムで定義されている型 dev_t を保持する
必要がある場合問題になります.なぜなら, 機種によっては dev_t 型が
int よりも小さい場合があるからです. しかし, 代わりに int
を使うことはできません. dev_t が int よりも大きいような
機種があるからです. 非標準の定義では, 全ての機種で安全に
使える型は存在しないのです. 非標準 C をサポートしつつ, かつ
こういう引数を渡す方法としては, Autoconf を使って dev_t の
幅を検査し, 適切な引数の型を選択するようにするしかないでしょう.
これは問題ではありません.
プロトタイプを認識しない標準以前のコンパイラをサポートするには, 以下のようなプリプロセッサマクロを使うと良いでしょう.
/* Declare the prototype for a general external function. */ #if defined (__STDC__) || defined (WINDOWSNT) #define P_(proto) proto #else #define P_(proto) () #endif |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
読者が自分のプログラムを作成する際、既知のコンフィギュレーションオプション
に対応するのであれば、#ifdef による条件付コンパイルよりも
if (... ) を使用してください。後者の方が、可能なコード経路の全てに
ついて、より綿密な検査を実行できるからです。
例えば、
if (HAS_FOO)
...
else
...
|
と書く方が、
#ifdef HAS_FOO
...
#else
...
#endif
|
より好ましいでしょう。
GCC などの現代的コンパイラであれば、どちらの場合も全く同じコードを 生成します。どうようの手法を使って、たくさんのプロジェクトで成功を 収めています。
すべての移植性にまつわる問題を解決する「銀の弾丸」ではありませんが、 この方針に従うことで、GCC プロジェクトだけでも、人日や人年の単位では ありませんが、多くの人時を浪費せずに済んでいます。
GCC のソースコードにある REVERSIBLE_CC_MODE のような
関数形式のマクロの場合には、単純には if( ...) 文で使うことは
できませんが、簡単な回避方法があります。以下の例のように、
もう一つ別のマクロ HAS_REVERSIBLE_CC_MODE を使います。
#ifdef REVERSIBLE_CC_MODE #define HAS_REVERSIBLE_CC_MODE 1 #else #define HAS_REVERSIBLE_CC_MODE 0 #endif |
| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |