C++
STL

STLの標準入出力にsetfill/setwをまとめて設定するヘルパー構造体&関数

More than 3 years have passed since last update.


はじめに

C++とSTL(Standard Template Library)で標準(エラー)出力(std::cout、std::cerr)を使用して整数などを出力するとき、出力形式を指定するにはstd::hexやstd::setw/setfillといった関数を利用します。

このstd::setw/setfillは一度出力すると消えてしまうので、出力する度に... << std::setw << std::setfill(...) << ...などと設定する必要があります。意味としては分かりやすいのですが、冗長で読みにくいコードとなってしまいます。

例えば0x1234という数値を16進数で出力する場、さらに8桁まで0で埋めて(0xNNNNNNNNの形式で)出力するには次のようにします。

#include <iostream>

#include <iomanip>

void main()
{
std::cout << "0x" << std::hex << std::setw(8) << std::setfill('0') << 0x1234 << std::endl;
}

0x00001234

1回の出力(1234)でもそれなりに長くなりました。次は複数の数値を0xNNNNNNNNの形式で出力しようとして以下のようなコードを書いてみます。

#include <iostream>

#include <iomanip>

void main()
{
// パターンA
std::cout << "パターンA" << std::endl;
std::cout << "0x" << std::setw(8) << std::setfill('0') << std::hex << 0x1111 << std::endl;
std::cout << "0x" << std::setw(8) << std::setfill('0') << std::hex << 0x2222 << std::endl;
std::cout << "0x" << std::setw(8) << std::setfill('0') << std::hex << 0x3333 << std::endl;
std::cout << "0x" << std::setw(8) << std::setfill('0') << std::hex << 0x4444 << std::endl;
// パターンB
std::cout << "パターンB" << std::endl;
std::cout << "0x" << std::setw(8) << std::setfill('0') << std::hex << 0x1111 << std::endl
<< "0x" << 0x2222 << std::endl
<< "0x" << 0x3333 << std::endl
<< "0x" << 0x4444 << std::endl;
}


出力結果

パターンA

0x00001111
0x00002222
0x00003333
0x00004444
パターンB
0x00001111
0x2222
0x3333
0x4444

ご覧の通り、setw等を全て指定したパターンAではどの出力も求める形式となっていますが、それらを2回目以降省略したパターンBではstd::hexのみ生き残っています。


std::setw/setfillをまとめる

上記ではstd::setw/setfillが出力の度に初期化されてしまうことを確かめました。ではまとめて設定するにはどうすれば良いのかと言えば、std::setw/setfillを真似て以下の様な構造体と関数を定義すれば解決します。


setfillw

#include <iostream>

#include <iomanip>

template <typename ELEMENT_TYPE>
struct _setfillw {
ELEMENT_TYPE c;
std::streamsize w;
_setfillw(ELEMENT_TYPE c, std::streamsize w) : w(w), c(c) {}
};

template <typename ELEMENT_TYPE>
inline _setfillw<ELEMENT_TYPE> setfillw(ELEMENT_TYPE c, std::streamsize w) {
return _setfillw<ELEMENT_TYPE>(c, w);
}

template <typename ELEMENT_TYPE>
inline std::ostream& operator << (std::ostream& os, const _setfillw<ELEMENT_TYPE>& manip)
{
return os << std::setw(manip.w) << std::setfill(manip.c);
}


使い方は次のようになります。蛇足ですが、"0x"の前にsetfill/wあるいはsetfillwを指定すると"0x"が0で8桁に揃えられるので注意して下さい。

void main()

{
// パターンA
std::cout << "パターンA" << std::endl;
std::cout << std::hex
<< "0x" << setfillw('0', 8) << 0x1111 << std::endl
<< "0x" << setfillw('0', 8) << 0x2222 << std::endl
<< "0x" << setfillw('0', 8) << 0x3333 << std::endl
<< "0x" << setfillw('0', 8) << 0x4444 << std::endl;
// パターンB
std::cout << "パターンB" << std::endl;
std::cout << std::hex
<< "0x" << setfillw('0', 8) << 0x1111 << std::endl
<< "0x" << 0x2222 << std::endl
<< "0x" << 0x3333 << std::endl
<< "0x" << 0x4444 << std::endl;

std::cout << "<PRESS ANY KEY>" << std::endl;
std::cin.get();
}


出力結果

パターンA

0x00001111
0x00002222
0x00003333
0x00004444
パターンB
0x00001111
0x2222
0x3333
0x4444
<PRESS ANY KEY>


備考

VS2013に付属のSTLでもsetfill/wはその名前のテンプレート関数と適当な名前のテンプレート構造体が定義され、別個にoperator <<を定義することで実装されています。テンプレート関数は不要そうですが、現バージョンのC++の仕様上、template <typename A>f(A a)f(1)で呼び出せますが、template <typename A> struct A {A() {...}};A<int>(1)としないとコンパイルエラーとなるのでヘルパー関数として利用されています。

上記の定義でも同様の理由(関数にすればテンプレート引数の指定を省略できる)で目的の名前の関数を定義しています。