to_stringとto_wstringはintなどを文字列にして返す標準ライブラリ関数。便利なのだが、charとwchar_tで分かれているので汎用コードを書きたいときにそのまま書きたくない。Windowsはもはやwchar_tが主流だしLinuxではcharが主流でwchar_tは4バイトになることが多い。よってこれらをそのまま使うと汎用性がなくなる。
テンプレート関数
これらの関数をテンプレート関数にしたいとき、テンプレート引数は2つになり、最初の引数はcharかwchar_t、次の引数はintやdoubleとなる。テンプレート関数は部分特殊化ができないので、これらの組み合わせすべて書くしかなくなって効率が悪い。
テンプレートクラス
テンプレートクラスは部分特殊化ができるので、これを利用して汎用関数をつくりたい。文字列クラスのデフォルトをWindowsならwchar_tとしそれ以外ならcharとして扱えば同じ関数記述にできる。
汎用テンプレートクラスの定義
1 2 3 4 5 |
template<typename C, typename T> struct class_stdToString { static_assert(sizeof(T) == 0, "char or wchar_t"); }; |
これは実体化されることを想定してないので、static_assert常に失敗。
次にcharの部分特殊化
1 2 3 4 5 6 7 8 |
template<typename T> struct class_stdToString<char, T> { static std::basic_string<char> call(const T& t) { return std::to_string(t); } }; |
同様にwchar_t
1 2 3 4 5 6 7 8 |
template<typename T> struct class_stdToString<wchar_t, T> { static std::basic_string<wchar_t> call(const T& t) { return std::to_wstring(t); } }; |
ここまではユーザは意識しないコード。次がユーザが呼ぶ関数。
1 2 3 4 5 |
template<typename C = SYSTEM_CHAR_TYPE, typename T> std::basic_string<C> stdToString(const T& t) { return class_stdToString<C, T>::call(t); } |
SYSTEM_CHAR_TYPEは事前にtypedefされているcharかwchar_t。
テストコード
1 2 3 4 5 6 7 8 9 10 11 12 13 |
TEST(stdosd, stdToString) { #ifdef _WIN32 EXPECT_STREQ(L"1", stdToString(1).c_str()); #else EXPECT_STREQ("1", stdToString(1).c_str()); #endif EXPECT_STREQ("0", stdToString<char>(0).c_str()); EXPECT_STREQ("-1", stdToString<char>(-1).c_str()); EXPECT_STREQ(L"0", stdToString<wchar_t>(0).c_str()); EXPECT_STREQ(L"-1", stdToString<wchar_t>(-1).c_str()); } |
bool追加
to_stringにはboolがなく、intとして解釈されるようなのでboolも完全特殊化で定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
template<> struct class_stdToString<char, bool> { static std::basic_string<char> call(const bool& t) { return t ? "true" : "false"; } }; template<> struct class_stdToString<char, bool> { static std::basic_string<char> call(const bool& t) { return t ? "true" : "false"; } }; |
ソース:https://github.com/ambiesoft/lsMisc/blob/master/stdosd/stdosd.h
考察
C++にはもともとstreamがあって、それを使えばすべて汎用関数で書けそう。しかしto_stringとかの実装はそうなってないようなのでスルー。
これとは逆の関数。文字列からintなどに変える関数は引数に文字列しか入らないので型の推論ができないのでそれぞれの型ごとに別関数にするしかないものと思われる。