蝉は、やがて死ぬる午後に気づいた。ああ、私たち、もっと仕合せになってよかったのだ。:2018年11月21日分

2018/11/21(Wed)

[プログラミング幻語C] 移植性の高いプログラムを書くためにint{8,16,32,64}_tを使えという解説は真実なのか?

前に書いた記事の補足、将来int4096_tを導入したくなった時long long long long long long long long intみたいな食堂でバイキング達が大声で歌いだしそうなシロモノを実装するよか *1intN_tの方をプリミティブ型にした方が都合がいいけど36bitアーキを考えると難しいよねって話、これには別解があってint_{fast,least}N_tの方をプリミティブ型にすれば可能ではあるよな。 まぁ将来的にそうなるんすかね、もはや興味ないし多分ワイその頃生きてないだろうから知らんけど。最近のワイのテーマソングはジミヘンの「今日を生きられない」。

こいつらは元々はC99で追加されたもので

という厳密にNビットでなくてよい詰め物ビットがあるかもしれない整数型、こいつら真面目に使ってるコードにワイもお目にかかったことが無いし、規格読んだことない人だと存在自体知らないかもね。

なんでこんなものがあるのかというと、そもそも36bitワードマシンなら2のべき数であるint{8,16,32,64}_tなんぞ実装できないし、実はC99の規格にはint{8,16,32,64}_tの実装はMUSTではないどころか、提供できない処理系はむしろ定義してはならないと仕様に書いてある (逆にint36_tなどを提供できるならそれは好きにしろと書いてある)。

つまりそもそもintN_tを使ったコードには仕様の時点で元から移植性なんて無いのですわ、ちゃんとgccのドキュメントにも 注意が書いてあるゾ。

If your C compiler and target machine do not allow integers of a certain size, the corresponding above type does not exist.

そのかわりint_{fast,least}{8,16,32,64}_tの実装は必須なのよね、だから前回記事のUNISYS社のメインフレームのような36bitワードマシンの事まで移植性にこだわるなら

の方を使うのがスジということになる *2

また2のべき乗アーキテクチャであっても安心はできない、64bitアーキの初期にはILP64(intも64bit)とかSILP64(shortまで64bit)というシロモノがあったわけで、こいつらもまたint16_tやint32_tを欠いてる可能性があるのだ。

つまりは「移植性のあるコードを書きたければint{8,16,32,64}_tを使え」なんて巷にはびこる解説は、あくまで「ILP32とLP64の間だけ移植性のある」コードであって 真に移植性のあるコードということであればint{8,16,32,64}_tが実装できない処理系まで考えて、int_{fast,least}{8,16,32,64}_tの方を積極的に使うべきなんだよな本当は。

まぁそれはあまりに現実的ではないし無理だけどね、その手の処理系ではコンパイル通らないint{8,16,32,64}_tを使ったコードの方世の中の大半を占めているわけでな。 全部書き換えてテストするんですかねストレージサイズとか境界値とか変わるし、定数マクロあるといっても大変よねこれ。

つまりはとっくの昔に手遅れであるし、今時そんな変態アーキテクチャとおつきあいする必要のごく限られた人間だけが力いっぱい壁殴ってればいいので忘れた方がマシですな。 よって結論としては「移植性のあるコードを書きたければintN_tを使え、 メインフレーマーは腕立て伏せでもしてろ」と解説を書き直せばいいだけだな(ぉ

ちなみに36bitワードなPDP-10にNなんとかBSDを移植する試みでは、すべてのコードのint{8,16,32,64}_tを書き換えるのは不可能と判断し

/* 7.18.1.1 Exact-width integer types */

#ifdef __GNUC__
typedef	__signed char		 __int8_t __attribute__ ((size (8)));
typedef	unsigned char		__uint8_t __attribute__ ((size (8)));
typedef	short int		__int16_t __attribute__ ((size (16)));
typedef	unsigned short int     __uint16_t __attribute__ ((size (16)));
typedef	int			__int32_t __attribute__ ((size (32)));
typedef	unsigned int	       __uint32_t __attribute__ ((size (32)));
#elif defined(__PCC__)
typedef	signed char		 __int8_t /* _Pragma ((size (8))) */;
typedef	unsigned char		__uint8_t /* _Pragma ((size (8))) */;
typedef	short int		__int16_t /* _Pragma ((size (16))) */;
typedef	unsigned short int     __uint16_t /* _Pragma ((size (16))) */;
typedef	int			__int32_t /* _Pragma ((size (32))) */;
typedef	unsigned int	       __uint32_t /* _Pragma ((size (32))) */;
#else
#error Need special types for compiler
#endif

と__attribute__(size)を使い無理矢理int{8,16,32,64}_tを実装することで動かそうとしていた。 まぁ完成しなかったからこれでイケたかは知らんそもそもint64_tが無いしなこれ。

*1:意味が判らない人はモンティ・パイソンの「スパムの多い食堂」を観よう。
*2:ものによっては GE-600みたいにcharが9bitになる以外short/int/long全部36bitの処理系もあるらしいゾ。