こういうビットマップ(1ピクセルにつき1ビット)を、整数の配列としてC言語のソースコード中に埋め込みたい:
□□□□□ ■■■■■ □■□■□ □■□■□ ■□□■■ □□□□□
2進数リテラルがあればそれを使うという手があるが、残念ながらC言語には2進数リテラルがない(ちなみに、C++にはC++14から2進数リテラルが入った。また、独自拡張で2進数リテラルが使えるコンパイラーもある)。
というわけで、自分で作ることにした。要件としては
- コンパイル時定数となる
- 桁を空白で区切れる
がある。
挙動としては 0b01101 を表したかったら VAL(_ X X _ X)
と書けば (((0 << 7) + (1 << 6) + (1 << 5) + (0 << 4) + (1 << 3) + 0) >> 3)
という式に展開される。
1の桁を表すために X
、 0の桁を表すために _
というマクロを定義していて行儀が悪いが、広く使われるわけでもないし、問題になれば #undef
すればいいので、まあ良しとする。
ビットシフトは + 演算よりも優先順位が低いので、本来は >>
の左側のカッコは必要ないが、カッコをつけないとお節介なコンパイラーさんが警告を出してくるのでカッコをつけることにした。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define CONCAT2(x,y) x ## y | |
#define CONCAT(x,y) CONCAT2(x,y) | |
#define VAL_END 0) | |
#define VAL0_END 0) >> 1 | |
#define VAL1_END 0) >> 2 | |
#define VAL2_END 0) >> 3 | |
#define VAL3_END 0) >> 4 | |
#define VAL4_END 0) >> 5 | |
#define VAL5_END 0) >> 6 | |
#define VAL6_END 0) >> 7 | |
#define VAL0(b) (b << 0) + VAL | |
#define VAL1(b) (b << 1) + VAL0 | |
#define VAL2(b) (b << 2) + VAL1 | |
#define VAL3(b) (b << 3) + VAL2 | |
#define VAL4(b) (b << 4) + VAL3 | |
#define VAL5(b) (b << 5) + VAL4 | |
#define VAL6(b) (b << 6) + VAL5 | |
#define VAL7(b) (b << 7) + VAL6 | |
#define VAL(x) ((CONCAT(VAL7 x,_END)) | |
#define X (1) | |
#define _ (0) | |
#if !defined(__cplusplus) | |
#define static_assert _Static_assert | |
#endif | |
static_assert(VAL(X) == 1, ""); | |
static_assert(VAL(_) == 0, ""); | |
static_assert(VAL(_ _) == 0, ""); | |
static_assert(VAL(_ X) == 1, ""); | |
static_assert(VAL(X _) == 2, ""); | |
static_assert(VAL(X X) == 3, ""); | |
static_assert(VAL(_ X _ X) == 5, ""); | |
static_assert(VAL(X _ _ _) == 0x08, ""); | |
static_assert(VAL(X X X X) == 0x0f, ""); | |
static_assert(VAL(_ X _ X X _ X _) == 0x5a, ""); | |
static_assert(VAL(X _ X _ X _ X _) == 0xaa, ""); | |
static_assert(VAL(X X _ X X _ X X) == 0xdb, ""); | |
static_assert(VAL(X X X X X X X X) == 0xff, ""); | |
static_assert(VAL(_ _ _ _ _) == 0x00, ""); | |
static_assert(VAL(X X X X X) == 0x1f, ""); | |
static_assert(VAL(_ X _ X _) == 0x0a, ""); | |
static_assert(VAL(_ X _ X _) == 0x0a, ""); | |
static_assert(VAL(X _ _ X X) == 0x13, ""); | |
static const unsigned char pi[] = { | |
VAL(_ _ _ _ _), | |
VAL(X X X X X), | |
VAL(_ X _ X _), | |
VAL(_ X _ X _), | |
VAL(X _ _ X X), | |
VAL(_ _ _ _ _), | |
}; |
ここまで書いてからググったら
という良さげな記事がヒットしたので、これから2進数リテラルを再発明しようという人は↑も参考にしたらいいと思う。
ISO 8988:1999 の規格では
一つの下線で始まるすべての識別子は,通常の名前空間及びタグ名前空間の双方におけるファイル有効範囲をもつ識別子としての使用に対して,常に予約済みとする。
とあるのでマクロ名に _ とするとは 規格 NG になります