stream parser
以前、stringstream で簡単 split という日記を書いたのですが、もうちょっと直感的な構文を使えないかと思い、実験してみました。
#include <iostream> #include <string> #include <sstream> template <typename T> struct string_to_any { void convert(const std::string& from, T& to) const { std::stringstream buf(from); buf >> to; } }; template <> struct string_to_any<std::string> { void convert(const std::string& from, std::string& to) const { if(!from.empty()) { to = from; } } }; template <typename T> class stream_parser { T& val_; const std::string delimiter_; string_to_any<T> string_to_any_; std::string fetch(std::istream& in_file) const { std::string result; std::string tmp; char ch; while(tmp.size() < delimiter_.size() && in_file.get(ch)) { if(ch == delimiter_[tmp.size()]) { tmp += ch; } else { if(!tmp.empty()) { result += tmp; tmp.clear(); } result += ch; } } return result; } public: stream_parser(T& val, const char* delimiter) : val_(val), delimiter_(delimiter) {} std::istream& execute(std::istream& in_file) const { string_to_any_.convert(fetch(in_file), val_); return in_file; } }; template <typename T> std::istream& operator>>(std::istream& in_file, const stream_parser<T>& parser) { return parser.execute(in_file); } template <typename T> stream_parser<T> get(T& val, const char* delimiter) { return stream_parser<T>(val, delimiter); } int main() { std::string str = "default value"; int num = -1; std::cin >> get(str, "::") >> get(num, "?n"); std::cout << "str=" << str << ", num=" << num << std::endl; }
うーん。この構文は微妙かなぁ。実行してみます。
$ ./a.out stream parser test::12345 str=stream parser test, num=12345 $ ./a.out :: str=default value, num=-1 $
文字列に空白も含めることができるし、空値の扱いも向上しているので、自前の設定ファイルをパースする程度ならまあ使えそうです。ただ、この構文を実現するためには、結構な呼び出し階層が必要になるんですね。マニピュレータとアプリケータの動きを勉強するには丁度良い課題でした。