flat7th

memo/20090908

created 2009-09-08 modified 2009-10-01 

ビットフィールド使うと移植性がなくなる件

リンク備考
mozilla C++ Portability Guide
vc++ は(vc8でも)ビットフィールドの基本型が異なると、つなげてパッキングしてくれない(→だからといって
uint8_t aaa:4;
uint8_t bbb:2;
uint32_t ccc:26;
を、
uint32_t aaa:4;
uint32_t bbb:2;
uint32_t ccc:26;
とかやっても、ダメぽ)。なんでかというと...

リンク備考
Sun Studio 12: C ユーザーズガイド 付録 E ISO/IEC C90 の処理系定義の動作
構造体メンバの配置
ビットフィールドを高位から埋めるか低位から埋めるかは環境依存であり、sunは高位から埋めてくれやがる、と。
C/C++ の仕様としては... 構造体の定義順でアドレスが増加しないといかん、ということらしい。でもその仕様はリトルエンディアンかビッグエンディアンかはリンクしないのでややこしいことになる。

リンク備考
JavaでビットフィールドこれはJavaのページだけど。
で、結論としては、移植性の観点からはこれが正解だと。

【結論】
構造体メンバの定義は最小単位でもバイトにして、バイトをまたがるメンバはマスクとシフトで乗り切る。
uint16_t や uint32_t の場合、シリアライズ、デシリアライズのときは、ちゃんとバイトオーダ変換する。

uint32_t aaa_bbb_ccc;

以上、調べた結論は「ビットフィールドは使わない」なんですが、どういう書き方をすれば、ほぼ統一できるのかなぁ、と考えて、やってみた案をメモ。

【没案】
・パケットフォーマットを決めるとき、32ビット区切りをまたぐフィールドは定義しない。
・バイト単位に収まらないフィールドがある32ビットを、ひとまとまりとして、uint32_t との union で定義する。で、メンバは32ビット内の低位から並べる。
・シリアライズ、デシリアライズのときは、uint32_t の値として、ちゃんとバイトオーダ変換する。

これ、結局ソースが煩雑になり、メリット皆無になります。
そもそもパケットのフォーマットどおりの構造体を定義するのはバッファポインタを構造体ポインタでキャストするといいじゃん、というアイデアが元だけど、もうその領域じゃなくなる。没。



追記:2009/10/01

gccには、ms-vc流のパッキングを行うコンパイラオプションやプラグマ指定があるらしいです。キーワードは

-m(no-)ms-bitfields
__attribute__((__ms_struct__))
#pragma ms_struct [on|off|reset]

あたりの模様。