目鼻がつきました。
GitHub - mattsan/BitOperation: operating bits classes
(2010/03/10追記:上記のリポジトリは随時更新しています。この記事を書いた当時の内容はこちらへ)
これはなに?
以前にも書いたことがあるように、いまの仕事では通信がらみということもあり開発するソフトでもビット列を扱うことが多々あります。
で、ビット操作が何度もソースコードに現れるんですが。一貫性のあるスッキリした書き方がなかなかできない。書き方に限れば書式のルールを決めることで多少は一貫性のあるコードにできるとは思いますが、それでも面倒くさいことには変わりなく。
そんなわけで。もっと楽に扱えるようにするクラステンプレート(と関数テンプレート)をC++で書いてみました。
使うために
ソースファイルは上記のGitHubのサイトから入手してください。本体はBits.hというファイルだけです。利用する場合はBits.hをインクルードしてください。
ネームスペースはemattsan::bits
です。
以下のコードではファイルのインクルードとネームスペースは省略して記述します。
小さいビットサイズの整数型
Signed
テンプレートとUnsigned
テンプレートで指定したビットサイズの整数値を扱う変数を定義できます。
Signed<3> a; // ビットサイズ3の符号付き整数(-4〜3) Signed<5> b; // ビットサイズ5の符号付き整数(-16〜15) Unsigned<2> c; // ビットサイズ2の符号なし整数(0〜3) Unsigned<4> d; // ビットサイズ4の符号なし整数(0〜15)
要は、指定したビット数でマスクされたされた状態が維持される整数型です。ただ符号付きの場合は単にマスクするだけでなく符号拡張します。
a = 1; //=> 1 a = 4; // => -4 a = 8; // => 0 a = 15; // => -1 c = 1; //=> 1 c = 3; //=> 3 c = 4; // => 0 c = 7; // => 3
内部では組み込みの整数型を使っていますが、テンプレート引数の第二引数で型を指定できます。その場合、クラスのサイズは指定した型と同じサイズになります。
sizeof(Signed<1, char>) // => sizeof(char) sizeof(Signed<1, short>) // => sizeof(short) sizeof(Signed<1, int>) // => sizeof(int) sizeof(Signed<1, long>) // => sizeof(long)
指定できる方はchar
,signed char
,unsigned char
,signed short
,unsigned short
,signed int
,unsigned int
,signed long
,unsigned long
の9種類です。型の指定を省略した場合はunsigned long
が指定されます。
実は。符号付き型と符号なし型を指定できるようにしていますが、符号付きの型でも符号なしの型でも違いはありません。
ビット列を連結する
これらの小さい整数型は連結することができます。
Signed<4> x(4); // 4 ( 0100b ) で初期化 Signed<4> y(2); // 2 ( 0010b ) で初期化 int n = (x, y); // => 42h ( 01000010b ) int m = (y, x); // => 24h ( 00100100b )
これを利用したRGB各5ビットのカラーコードを作成する関数の例です。
unsigned int make_rgb555(unsigned int r, unsigned int g, unsigned int b) { const Unsigned<5> r5(r); const Unsigned<5> g5(g); const Unsigned<5> b5(b); return (r5, g5, b5); }
ビット列を分解する
連結ができるなら分解もしたいところ。
連結した状態で代入するともとの値を分割できます。
Unsigned<5> r, g, b; (r, g, b) = 0x14a5; //14a5h == 0001010010100101 / a => 5, b => 5, c => 5
使わないビットがあるんだけど
ビット列の中にはRESERVEDになっていて使われていないビットが含まれていることがあります。
そんな場合はreserve
テンプレートを使えばOK。
Unsigned<2> a(1); Unsigned<2> b(1); int n = (a, reserve<2>, b); // n => 11h ( 010001b )
分割にも使えます。その場合その場所のビットは捨てられます。
(a, reserve<2>, b) = 0x77; // a => 3, 途中2ビットは捨てられる, b => 3
このあとは
効率には気をつかったつもりですがまだ計測できていないのでそれをする予定。速度はそこそこ気にしたつもりですが、メモリの利用がどうなっているのかが不明。
あと、コードが冗長になっている部分があるのでそこをどうにか整理したいところ。
余談
別の件で偶然次のようなエントリに出会いました。
Expression Template - Faith and Brave - C++で遊ぼう
構造上はこのExpression Templateと同じみたいな雰囲気。
アプローチ間違えてなかったとちょっとほっとした。