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で追加されたもので
- int_fastN_t … 最速最小幅指定整数型 、その処理系で最低Nビットの幅を「最速で」処理できる大きさ
- int_leastN_t … 最小幅指定整数型、その処理系で最低Nビットの幅を処理できる任意の大きさ
という厳密に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ワードマシンの事まで移植性にこだわるなら
- int_{fast,least}8_t = char(9bit)
- int_{fast,least}16_t = short(18bit)
- int_{fast,least}32_t = int(36bit)
- int_{fast,least}64_t = long long int(72bit)
の方を使うのがスジということになる *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が無いしなこれ。