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

とくにあぶなくないRiSKのブログ

危ないRiSKのブログだったかもしれない。本当はRiSKだけどググラビリティとか取得できるIDの都合でsscriskも使ったり。

列挙型

【C++】型安全なビットフラグと簡潔な列挙型 (移転したようです。新しい場所はここ【C++】型安全なビットフラグと簡潔な列挙型 - Weblog)
いろいろないなーと。

違いはenum識別名が最初からtypedefしてあったり、
列挙子が型をもっている事ぐらいでしょうか。

typedef されてないですよ。多分Cでいう

// C
int main()
{
 enum e {A, B};
 enum e a = A; // enum いる
 typedef enum e e;
 e b = B;      // enum いらない
}

こういうことだと思うんだけど。C++ で 宣言で enum がいらないのは typedef じゃないです。

という訳で、上記の2点を解決するため次のようなユーティリティを使っています。

 inline Enum operator|(Enum x,Enum y){return static_cast<Enum>(x|y);}\
 // :
 OPTION1 = 1,
 OPTION2 = 2,
 OPTION3 = 4
 // :
 function(ws::OPTION1 | ws::OPTION2 | ws::OPTION3); //WindowStyleBitとWindowStyle::Enum以外の値を渡すとエラー

operator| が無限再帰しないの?とかあるんですが,それより
(OPTION1 | OPTION2 | OPTION 3) は int でいう 7 にしたいのでしょうけど,それに対応する列挙子がないです。で,その 7 を static_cast してる。これはアウトです。

追記:

じゃあどうしたらいいの?ってこと書いてなかった。
strong typedef を使えばいいかも。
Boost C++ Libraries - boost/strong_typedef.hpp
Serialization - BOOST_STRONG_TYPEDEF

#include<boost/strong_typedef.hpp>

namespace WindowStyleBit{
 
 BOOST_STRONG_TYPEDEF(int, WindowStyle)

 WindowStyle const OPTION1(1);
 WindowStyle const OPTION2(2);
 WindowStyle const OPTION3(4);
 
}
typedef WindowStyleBit::WindowStyle WindowStyle;

void function(WindowStyle style)
{
 using namespace WindowStyleBit;

 if(OPTION1 & style){}
 if(OPTION2 & style){}
 if(OPTION3 & style){}
}

namespace ws = WindowStyleBit;

int main()
{
 function(WindowStyle(ws::OPTION1 | ws::OPTION2 | ws::OPTION3));
}

なんかいまいちだな。

追記2:

しっかり問題を把握して解決してみる。

anonymouse_userさんがしたいこと
  • 型安全
  • ビット演算できる
  • namespaceの省略
  • スケーラビリティを持たせる。
  • チームに強制できるようにする
anonymouse_userさんの解法の問題点
  • マクロを使っている
  • それによって新しい文法を持ち込んでしまっている
  • 未定義動作をするコードで移植性がない

結果,「スケーラビリティを持たせる。チームに強制できるようにする」から離れている。

boost/strong_typedef.hppの問題点
  • マクロ使っている
  • ビット演算のオーバーロードがない。致命的。
こだわる必要のないこと
  • enum
    • 未定義動作を持ち込む原因になっている
    • enumを使わなくても,定数を列挙することはできる
bit_flagクラステンプレート書いた

これを使えば幸せになれる!

  • 型安全
  • ビット演算できる
  • namespaceの省略も自由
  • スケーラビリティがある
  • マクロによる謎の文法を覚える必要が無く,普通のテンプレート
    • テンプレートを知らない人でも使い方はすぐ覚えられる
    • どうせ覚えるなら謎の文法を覚えるよりテンプレートを覚えた方がいい
  • 使い方が簡単。チームに強制させやすい
template<class Tag, class Base = int>
class bit_flag
{
 Base value;
public:
 typedef Base value_type;
 bit_flag():value(){}
 bit_flag(bit_flag const & rhs):value(rhs.value){}
 explicit bit_flag(value_type const & value):value(value){}
 Base to_base_type(){return value;}
 bit_flag operator|(bit_flag const & rhs)const{return bit_flag(value | rhs.value);}
 bit_flag operator&(bit_flag const & rhs)const{return bit_flag(value & rhs.value);}
 bit_flag operator^(bit_flag const & rhs)const{return bit_flag(value ^ rhs.value);}
 bit_flag operator~()const{return bit_flag(~value);}
 bool operator==(bit_flag const & rhs)const{return value == rhs.value;}
 bool operator!=(bit_flag const & rhs)const{return !(*this == rhs);}
 // explicit operator bool() (use safe bool idiom)
private:
 typedef void (bit_flag::*safe_bool)()const;
 void for_safe_bool()const{}
public:
 operator safe_bool()const{return value ? &bit_flag::for_safe_bool : 0;}
};

namespace WindowStyleBit{
 
 // structなタグを作って、typedefするだけ。超簡単。
 struct WindowStyleTag;
 typedef bit_flag<WindowStyleTag> WindowStyle;
 WindowStyle const OPTION1(1),
                   OPTION2(2),
                   OPTION3(4);
 
}

int main()
{
 using namespace WindowStyleBit;
 WindowStyle a;                // デフォルトコンストラクタ
 WindowStyle b(42);            // base typeを取るコンストラクタ
 WindowStyle c(b);             // コピーコンストラクタ
 int value = b.to_base_type(); // to_base_type
 // int value2 = b;               型変換なし。ちゃんとコンパイルエラーになってくれる
 WindowStyle d = b | OPTION1;  // operator|
 WindowStyle e = b & OPTION2;  // operator&
 WindowStyle f = b ^ OPTION3;  // operator^
 WindowStyle g = ~b;           // operator~
 if(b){}                       // explicit operator bool
 // int value3 = b;               explicit operator bool でちゃんとコンパイルエラーになってくれる
 if(b == OPTION1){}            // operator==
 if(b != OPTION2){}            // operator!=

 struct AnotherTag;
 typedef bit_flag<AnotherTag> Another;
 Another h(42);
 // Another i(b);              // 型が違う。コンパイルエラー
 // if(b == h){}               // 型が違う。コンパイルエラー
 if(b.to_base_type() == h.to_base_type()){} // これならOK
}