非標準処理系と移植性

C/C++の移植性を考える場合、一般論としては、非標準処理系や処理系の不具合は対象にすべきではありません。そうした話を持ち出すと、結局のところ標準規格は一切機能しなくなり、何が起きても不思議ではない状況が発生してしまいます。

ただ、現実的には、主要な処理系、あるいは大多数の処理系が規格にしたがっていないような場合もあります。例えば、C++のexportとか、国際文字名とかがそれにあたります。そうした場合には、「規格がこうだから」という話をいくらしても、現実には役に立ちません。つまり、こうした場合には、いわゆる一般論は通用しなくなるわけです。

もうひとつ、必ずしも主要とはいえないけれども、今自分が相手をしている処理系、あるいは今後相手をすると予想される処理系が、規格とは異なる振る舞いをする場合には、当然それらに対する配慮を行わなければ、必要な移植性を持たせることができなくなります。

少し具体的な話をしましょう。[迷信] とりあえず memset で初期化で書いたように、集成体のゼロクリアは、memsetではなく、 { 0 }(C++であれば {} でもよい)を使うべきであるという主張に対して、「それは処理系に依存する」と反論する人がいます。しかし、JIS X3010:2003の6.7.8によると...


 

ということで、明示的に初期値を指定していない要素がゼロクリアされることは規格上保証されています。けれども、現実には、非標準処理系や不具合のある処理系で、そうはならないことも起こり得るでしょう。しかし、それを以って、「処理系依存だ!」と主張するのはおかしな話です。前述したように、非標準処理系や処理系の不具合を相手にしだすと、何一つ保証される振る舞いはなくなってしまうからです。

この例でいえば、もし自分が相手をしている処理系が規格通りに振舞わないのであれば、ゼロクリアにmemsetを使うかどうかは別として、何らかの方法で個別対応せざるを得ないでしょう。本当にゼロクリアが必要な状況であれば、例外的な動作をする"その処理系"のための対策として、memsetによるゼロクリアで問題がないことを機械語レベルで確認した上で、そのように記述するのであれば、特に問題はありません。ただし、適切に#ifdef指令で囲むなどして対応すべきです。

特定処理系に個別対応するためには、#ifdef等の前処理指令に頼らざるを得ません。そうではなく、問答無用でmemsetを使ってしまえば、"その処理系"に対してはよくても、他の処理系への移植性を捨てることになります。

もうひとつの選択肢としては、処理系に対する仮定を明確にするということがあります。つまり、ポインタや浮動小数点数であっても、memsetによるゼロクリアが通用するような内部表現になっていることを、移植上の制限としてしまうということです。もちろん、これはローカルルールであり、一般論にはなり得ません。

この記事のトラックバックURL:

http://www.kijineko.co.jp/trackback/734