あいまいなタイトルで申し訳ありません。
よく見かける手法なのですが、具体的な名前を見つけられませんでした。ご存じの方いたらご教示ください。
アレって何・・・
C/C++で以下のようなオプション的なものの定義をよく見かけませんか。
# define DO_A 0x0001
# define DO_B 0x0002
# define DO_C 0x0004
# define DO_D 0x0008
# define DO_E 0x0010
# define DO_F 0x0020
doSomething(DO_A | DO_B | DO_F); // この引数の与え方をアレと言っている!!!
ポイントは、以下です。
- 各オプションの値が2進数にした時に0001, 0010, 0100, 1000,,,と相異なる1bitのみが1になるように定義されていることと。
- 関数に与えるときに各オプションを " | "(bitwise OR)で与えること
恥ずかしながら、今までこの手法の実装方法を知らなかったのでまとめの意味で説明してみます。
bitwise OR (念の為)
C/C++でif文でよく使うORは " || "(logical OR) ですが、ここで使うのは " | "(bitwise OR) です。
これはbit単位でORを取るものなので、
0001 | 0010 = 0011
1100 | 0011 = 1111
ということです。logical OR と bitwise OR を念のためコードで比較してみました。
# include <cstdio>
int main (void) {
const int X = 0x02;
const int Y = 0x10;
printf("logical OR: 0x%x\n", X || Y); // %xは16進数表示
printf("bitwise OR: 0x%x\n", X | Y);
return 0;
}
logical OR: 0x1
bitwise OR: 0x12
bitwise AND で与えられたオプションを分解
ではオプションを受け取った側はこれをどう分解し、解釈すればい良いのでしょうか。
これは bitwise OR の対となる "&" (bitwise AND) を使うと極めて簡単に書くことが出来ます。
# include <cstdio>
const int DO_A = 0x01;
const int DO_B = 0x02;
const int DO_C = 0x04;
void doSomething(const int option) {
if (option & DO_A) { // これだけで DO_A が option に含まれているか判定できる
printf("do A\n");
}
if (option & DO_B) {
printf("do B\n");
}
if (option & DO_C) {
printf("do C\n");
}
}
int main (void) {
doSomething(DO_A | DO_C);
return 0;
}
do A
do C
doSomething関数が受け取る DO_A | DO_C
の値は何でしょう?
0x01 | 0x04 なので、0001 | 0100 = 0101 → 0x05 です。これが doSomething関数の option
に格納されます。
では if の中でやっている if (option & DO_A)
は何をやっているのでしょうか。
0x05 & 0x01 → 0101 & 0001 = 0001 → 真、となります。
では if (option & DO_B)
は?
0x05 & 0x02 → 0101 & 0010 = 0000 → 偽、となります。
まとめ
というわけで、引数に与えるときは bitwise OR、与えられたものを分解解釈するときは bitwise AND で簡単にできるということでした。
ご参考になれば幸いです。
なお、定数を定義する際は以下のようにビットシフトを用いた書き方もよく見ます。
# include <cstdio>
const int P = 1 << 0; // << で 1を左へビットシフト
const int Q = 1 << 1;
const int R = 1 << 2;
const int S = 1 << 3;
const int T = 1 << 4;
const int U = 1 << 5;
int main (void) {
printf("P: 0x%04x\n", P);
printf("Q: 0x%04x\n", Q);
printf("R: 0x%04x\n", R);
printf("S: 0x%04x\n", S);
printf("T: 0x%04x\n", T);
printf("U: 0x%04x\n", U);
return 0;
}
P: 0x0001
Q: 0x0002
R: 0x0004
S: 0x0008
T: 0x0010
U: 0x0020
おわり