文字列から何かに変換する・何かから文字列に変換する

ここでいう「何か」は型のこと。いやそんなん stringstream かませばいいじゃんというのは正解というか大正解なんだけど stringstream は生成するにしてもその大きさにしてもけっこう「重い」し変換操作を一連の手続きとして閉じ込めてしまうことに意義があるので stringstream を cache しつつ wrap する class を書いてみた。

いや最初ベタに関数で num2str とか書いてたんだけど std::wstring に対応するには…、 traits か !! とか無駄に張り切って書いてたら class 化せざるを得なくなって ( template 引数の default 値は class template じゃないと指定できない ) 最終的に basic_string の二番煎じみたいになったというオチ。余談だけど traits って要は型を data として扱って必要に応じてその data を切り替えようという考え方だよな。実に良くできているが思想や成立経緯から説明しないとその完成形の複雑さも相まって理解が難しいものだと思った。いやまぁ STL の reverse engineering でさわりだけなら習得できないこともないんだけどというか web 上の適切な document が見つからなかったのでそうせざるを得なかったんだけど。

以下のスーパー pre 記法内の code について public domain におかれている ( おれは著作権を放棄するうえに著作者人格権を行使することはない ) という点 ( こんなん誰でも思いつくから著作権とか発生しねーよボケというのは認めるけどそれはおいといて ) と無保証という点がわかっていれば ( comment で同じ事を英語で書いている ) 煮ようが焼こうが蒸そうがご自由に。今までこういうの指定してこなかったけどうちで晒してるのは基本 public domain かつ無保証なのでそういうことで。ってことでいいのかな。

/*
 * converter.hpp
 * type converter by routing through std::string / std::wstring
 *
 * written by janus_wel<janus.wel.3@gmail.com>
 * This source code is in the public domain, and has NO WARRANTY.
 * */

#ifndef CONVERT_HPP
#define CONVERT_HPP

#include <sstream>
#include <string>

namespace util {
    namespace string {
        template<typename Ch> struct convert_traits {};
        template<> struct convert_traits<char> {
            typedef char              char_type;
            typedef std::string       string_type;
            typedef std::stringstream stream_type;
            static const char_type* null() { return ""; }
        };
        template<> struct convert_traits<wchar_t> {
            typedef wchar_t            char_type;
            typedef std::wstring       string_type;
            typedef std::wstringstream stream_type;
            static const char_type* null() { return L""; }
        };

        template<typename Ch, typename Tr = convert_traits<Ch> >
            class basic_converter {
                private:
                    typename Tr::stream_type ss;

                public:
                    template<typename T>
                        typename Tr::string_type strfrom(T num) {
                            static const typename Tr::string_type null = Tr::null();
                            ss.clear(); ss.str(null);

                            ss << num;
                            return ss.str();
                        }

                    template<typename T>
                        T strto(const typename Tr::string_type& str) {
                            ss.clear();

                            ss.str(str);
                            T num;
                            ss >> num;
                            return num;
                        }
            };

        typedef basic_converter<char>    converter;
        typedef basic_converter<wchar_t> wconverter;
    }
}

#endif // CONVERT_HPP

でもって以下のように使う。

#include "convert.hpp"
#include <iostream>

util::string::converter  conv;
util::string::wconverter wconv;

int inum = conv.strto<int>("1000");
std::cout << inum << ", " << conv.strfrom(inum) << ", ";
std::wcout << wconv.strfrom(inum) << std::endl;

float fnum = conv.strto<float>("3.141592");
std::cout << fnum << ", " << conv.strfrom(fnum) << ", ";
std::wcout << wconv.strfrom(fnum) << std::endl;

もちろん stringstream の wrapper なだけなのでその型を引数に取る operator <<() と operator >>() が定義されていれば組み込み型以外に対しても使える ( 以下の例はテケトーすぎるけど ) 。

// main.cpp

#include "converter.hpp"
#include <iostream>
#include <istream>
#include <ostream>

namespace math {
    struct Complex {
        int r;  // real number
        int i;  // imaginary number
        Complex() {};
        Complex(int r, int i) : r(r), i(i) {};
    };

    std::ostream& operator <<(std::ostream& out, const Complex& comp) {
        return out << comp.r << '+' << comp.i << 'i';
    }
    std::istream& operator >>(std::istream& in,  Complex& comp) {
        return in >> comp.r >> comp.i;
    }
    std::wostream& operator <<(std::wostream& out, const Complex& comp) {
        return out << comp.r << '+' << comp.i << 'i';
    }
    std::wistream& operator >>(std::wistream& in,  Complex& comp) {
        return in >> comp.r >> comp.i;
    }
}

using namespace std;
using namespace util::string;
using namespace math;

int main(const int argc, const char* argv[]) {
    converter  conv;
    wconverter wconv;
    Complex compsrc(1, 3);

    string compstr = conv.strfrom(compsrc);
    cout << compsrc << ", " << compstr << ", " << conv.strto<Complex>(compstr) << ", ";
    wcout << wconv.strfrom(compsrc) << endl;

    return 0;
}

というわけで何かから文字列はともかく ( どこかに出力するなら直接 stream に流し込んだ方がはやい ) 文字列から何かに変換するにはけっこう使えるはず。