こんにちは。田原です。

今回はTheolizer®から離れてC++11の話題です。
C++11でscoped enumと言うバグ検出力を高めたenum型が追加されました。安全なのですが、中身を確認したい時、static_cast<int>()などが必要なのでちょっと面倒です。従来通りstd::cout << enum変数 << “\n”;できるようなツールを作りました。

ご自由に使って下さい。(ただし、私からの保証はありませんので、ご自身の責任でお願いします。)

1.使い方
使い方は簡単です。ostream-enum.hをインクルードするだけです。後はscoped enumも普通に出力できるようになります。

以下、Windows 10のコンマンド・プロンプトにおけるVisual Studio 2015とMinGW 5.4.0でのコンパイルと実行結果です。(両方とも事前にパスを通しています。)

Visual Studio 2015

C:\ostream-enum>cl main.cpp  /EHsc
Microsoft(R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
main.obj

C:\ostream-enum>main
aNormalEnum=1
aScopedEnum=2

MinGW 5.4.0

C:\ostream-enum>g++ main.cpp -std=c++11

C:\ostream-enum>a.exe
aNormalEnum=1
aScopedEnum=2

 

2.ソース
まず、実装は関数テンプレートなのでヘッダ・オンリーです。

考え方は単純です。
std::ostream& operator<<(std::ostream&, 型)というstd::ostreamに対する出力演算子を、enum型に対してオーバーロードしています。

ただし、「enum型」は例えば「int型」のように特定の1つの型ではありませんので、単純にはオーバーロードできません。関数テンプレートにすれば複数の型に対して定義できますが、関数テンプレートは部分特殊化できません。従って、enum型全般に対して特殊化する(別途定義する)ことがC++言語仕様としてはできないのです。

しかし、世の中には頭の良い人達がいるもので、特定の型群に対して関数テンプレートでオーバーロードすると言う荒業(?)で事実上の部分特殊化する方法が見つかっています。SFINAEとテンプレート・メタ・プログラミングを利用した難しい使い方です。興味のある方は上記のリンク先とQiitaのこの記事が参考になりますので、ご参照下さい。

この原理を利用して、19行目~24行目でtEnumがenum型の時だけオーバーロードしました。

C++11で<type_traits>と言うヘッダが追加されています。これは型に対して色々な操作を行うライブラリです。
今回はこの中から、std::is_enum<T>とstd::underlying_type<T>を使いました。

項目 説明
std::is_enum<T> Tがenum型ならvalueがtrueになる
std::underlying_type<T> Tはenum型でその内部表現の整数型がtypeとして定義される

std::is_enum<T>::valueは、Tがenum型ならtrueになり、そうでないならfalseとなります。
enum型はその値を保存するために内部的になんらかの整数型を割り当てています。std::underlying_type<T>::typeはその整数型になります。従って、std::underlying_type<T>::type型へのstatic_castは安全に行えます。