せっかくなので公開してみます。
とりあえずコードを。(このヘッダだけ include すれば使えるようになります。VS2013で確認済み)
▼(2015/7/6)h2so5さんの指摘修正ver さて、単純なenum値⇔文字列変換を行うマクロであれば、検索すればいくつかヒットします。
http://www.singularpoint.org/blog/c/c-e ... ing-macro/
http://qiita.com/nunu-e64/items/e29075bdd82fad8ec28c
が、どうにも機能不足を感じます。
私はライブラリ書きで、人様に公開するモノとしては最低限以下のような条件を揃えたいと思うところでした。
・文字列と enum 値を相互変換できること。
・型安全なビット演算ができること。
・できるだけオリジナルの enum に近い記述で定義できること。
・メンバに Doxygen コメントを振れること。
・他の enum 値で値を定義できること。
まず、今回紹介するマクロを使った enum 定義の基本形です。
// 定義
EX_ENUM(Color)
{
Red = 0,
Green,
Blue,
};
EX_ENUM_DECLARE(Color);
// 使う
Color c = Color::Red;
まずは、話を聞いてくれ。
_、_
( ,_ノ` )
ζ
[ ̄]'E
■文字列と enum 値を相互変換したい
XML 等のマークアップを利用するときに enum値⇔文字列 の変換が必要となるケースは割と良くあったりします。
C++ は C# とかみたいに enum がリッチじゃないので、
変換テーブルを書いたりと涙ぐましい努力が必要になります。
さて、次のように EX_ENUM_REFLECTION を定義に差し込みます。
EX_ENUM(Color)
{
Red = 0,
Green,
Blue,
};
EX_ENUM_REFLECTION(Color, Red, Green, Blue); // enum メンバを並べる
EX_ENUM_DECLARE(Color);
// 使う
Color c = Color::Red;
std::string str = c.ToString(); // 文字列へ → "Red"
c = Color::Parse("Blue"); // enum 値へ → Blue
■型安全なビット演算がしたい
まずは普通の enum の問題点。
例えば…
enum Color
{
Color_Red = 0x01,
Color_Green = 0x02,
Color_Blue = 0x04,
};
void test(Color c) { }
int main()
{
test(Color_Red | Color_Green); // error
}
ビット和の結果は int なので、test の引数型を int にしなければなりません。
しかしそれでは test(123) のように書かれてしまってもコンパイルエラーとして検出できない (型の安全性が低い) ですし、
インテリセンスに enum 型が出てこないので test() の引数に何を指定するべきかすぐにわかりません。(「定義へ移動」できないのが辛い…)
この問題は C++11 の enum class でも同様です。(こっちはビット和自体禁止しているようですが)
EX_ENUM_FLAGS マクロで enum を定義することで、enum class のような使用感でこの問題を解決できます。
EX_ENUM_FLAGS(Color)
{
Red = 0x01,
Green = 0x02,
Blue = 0x04,
};
EX_ENUM_FLAGS_DECLARE(Color);
void test(Color c) { }
int main()
{
test(Color::Red | Color::Green);
}
https://wiki.qt.io/QFlags_tutorial
冗長な部分をマクロで隠しているだけです。
■ビット演算でも文字列変換したい
定義に EX_ENUM_FLAGS_REFLECTION マクロを差し込みます。
EX_ENUM_FLAGS(Color)
{
Red = 0x01,
Green = 0x02,
Blue = 0x04,
};
EX_ENUM_FLAGS_REFLECTION(Color, Red, Green, Blue); // enum メンバを並べる
EX_ENUM_FLAGS_DECLARE(Color);
// 使う
Color c = Color::Red | Color::Blue;
std::string str = c.ToString(); // 文字列へ → "Red|Blue"
c = Color::Parse("Red|Green"); // enum 値へ → Red|Green
トークンの間には空白を許可しています。
■できるだけオリジナルの enum に近い記述で定義したい
先頭で紹介したリンク先では のように、可変長引数を利用して1行で定義していました。
しかし、(個人的な主観に依るところも大きいですが)この記述は enum を、少なくともプログラマにとって
何か意味のあるブロックを定義しているとは一見わかりにくいです。
{ } は C++ プログラマにとって大きな意味を持つトークンです。
ですので、あえてマクロの外側に出しています。
■メンバに Doxygen コメントを振りたい
enum コメントの理想は以下のような感じです。
/// 色を示す列挙型
enum Color
{
Red, ///< 赤
Green, ///< 緑
Blue, ///< 青
/// 1行では足りない場合は
/// 複数行に分けでコメントを振れる。
All = Red | Green | Blue,
};
つまり、以下のようなコメントはどう頑張っても拾ってくれません。
EX_ENUM は Doxygen の設定 (Preprosessor) で "EX_ENUM_DOXYGEN" シンボルを定義するだけで
理想通りコメントを振れます。
/// 色を示す列挙型
EX_ENUM(Color)
{
Red, ///< 赤
Green, ///< 緑
Blue, ///< 青
};
EX_ENUM_DECLARE(Color);
■他の enum 値で値を定義したい
enum としては当然の機能ですが、先の例のように関数マクロを使用しているとこれも通常は不可能です。
しかし、EX_ENUM は実際のメンバ定義をマクロの外側に出しているので問題ありません。
■最後に
テストプロジェクトも置いておきます。 原本はこちらです。
https://github.com/lriki/Lumino.Core/bl ... xtension.h
先頭に張り付けた EnumExtension.h は、こちらを STL 用に修正したものです。
今後問題があればこちらに修正が入りますので、
もしご利用いただける場合は必要に応じて差分をマージしてください。
・・・でも、こんなメタメタしい機能、boost にあってもよさそうだけど・・・私の探しが足りないだけでしょうか?