読者です 読者をやめる 読者になる 読者になる

nirasan's tech blog

趣味や仕事の覚え書きです。Linux, Perl, PHP, Ruby, Javascript, Android, Cocos2d-x, Unity などに興味があります。

Go 言語の列挙型的な定数と定数でのフラグ管理

列挙型的定数

  • Go では const と iota を使って列挙型的な定数を定義する
    • iota は const 宣言開始時に 0 になり、定数を定義する毎にインクリメントする
    • 定数定義は値を指定しないと直前の定数と同じ値になるので iota と組み合わせることで連続した値が定義される
// 定義
type Flag int
const (             // iota = 0 (仮想コード)
  Flag1 Flag = iota // Flag1 = iota; iota++
  Flag2             // Flag2 = iota; iota++
  Flag3             // Flag3 = iota; iota++
)
// 利用例
f := Flag1
if f == Flag1 {
  // Flag1 の場合の処理
}

列挙型的定数を使ったフラグ管理

  • 列挙型的な定数の定義時に一工夫すると定数をフラグ的に管理できるようになる
// 定義
type Flag int
const (
  Flag1 Flag = 1 << iota // 1 << 0 (10進数: 1, 2進数: 00000001)
  Flag2                  // 1 << 1 (10進数: 2, 2進数: 00000010)
  Flag3                  // 1 << 2 (10進数: 4, 2進数: 00000100)
  Flag4                  // 1 << 3 (10進数: 8, 2進数: 00001000)
)
// 利用例
f := Flag1
if f & Flag1 != 0 {
  // Flag1 の場合の処理
  // &(and) 演算子で双方にビットが立っていた場合に 0 以外が返るので Flag1 が有効かどうかの判定ができる
}
if f & (Flag1 | Flag2) != 0 {
  // Flag1 か Flag2 の場合の処理
  // |(or) 演算子で Flag1 かつ Flag2 の値を作ることができるので複数のフラグの判定処理がちょっと楽になる
}

ひとつの定数で複数種類のフラグを管理する

  • 例えば文字の色と書体を同一の方で管理するような場合
type Flag int
const Flag_N_Start = 0
const (
    Flag1 Flag = 1 << (iota + Flag_N_Start) // 1 (00000001)
    Flag2                                   // 2 (00000010)
    Flag3                                   // 4 (00000100)
    Flag_N_Bits = iota                      // 3 (Flag_N で 3bit 使っている)
    Flag_N_Mask = 1 << Flag_N_Bits - 1      // 7 (00000111) (Flag_N 用のビットマスク)
)

const Flag_A_Start = Flag_N_Bits                       // Flag_A の開始ビット
const (
    FlagA Flag = 1 << (iota + Flag_A_Start)              // 1 << (0 + 3) =  8 (00001000)
    FlagB                                                // 1 << (1 + 3) = 16 (00010000)
    FlagC                                                // 1 << (2 + 3) = 32 (00100000)
    Flag_A_Bits = iota                                   // 3 (Flag_A で 3bit 使っている)
    Flag_A_Mask = (1 << Flag_A_Bits - 1) << Flag_A_Start // 56 (00111000) (Flag_A 用のビットマスク) (使用ビット分だけマスクを作って開始ビット分だけ左シフトする)
)

f := (Flag1 | FlagB)
if f & (Flag1 | FlagA) != 0 {
    // Flag1 か Flag2 だった時の処理
}