C言語でなんちゃって2進数リテラル

こういうビットマップ(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 すればいいので、まあ良しとする。

ビットシフトは + 演算よりも優先順位が低いので、本来は >> の左側のカッコは必要ないが、カッコをつけないとお節介なコンパイラーさんが警告を出してくるのでカッコをつけることにした。

#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(_ _ _ _ _),
};
view raw bits.c hosted with ❤ by GitHub

ここまで書いてからググったら

という良さげな記事がヒットしたので、これから2進数リテラルを再発明しようという人は↑も参考にしたらいいと思う。

Spread the love

C言語でなんちゃって2進数リテラル” に1件のフィードバックがあります

  1. nogami

    ISO 8988:1999 の規格では
    一つの下線で始まるすべての識別子は,通常の名前空間及びタグ名前空間の双方におけるファイル有効範囲をもつ識別子としての使用に対して,常に予約済みとする。
    とあるのでマクロ名に _ とするとは 規格 NG になります

    返信

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です