コンテナをストリーム出力
(vectorとかlistとか)コンテナの中身を確認したくてストリームに出力しようと思っても、
コンテナ自体にはoperator<<が定義されていないのでそのままostreamに出力することはできません。
だからといってコンテナの要素をイテレータでたどるために毎回
for(my_container_t::iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it;
}
とか書くのは面倒です。
かといって上のコードをそのまま関数にするのもstd::coutしか使用できないってことで汎用性がないうえ、
せっかくのoperator<<の仕組みを使えないのは残念です。
(他にはstd::ostream_iteratorを使う方法
std::copy(vec.begin(), vec.end(), std::ostream_iterator<my_container_t::value_type>(std::cout));
もありますね。)
コンテナの要素をよしなに出力してくれて、出力先は好きなostreamを使えて
operator<<の形で書ける何かがあると嬉しいですね。
to_ostream_outputtableというクラスを作りました。
こいつを使えば、コンテナをストリームに出力可能にすることができます。
#include <boost/test/minimal.hpp> #include <iostream> #include <list> #include <string> #include <sstream> #include <vector> #include <hwm/to_ostream_outputtable.hpp> struct TestClass { int age_; std::wstring name_; TestClass(int age, std::wstring const &name) : age_(age) , name_(name) {} template<class CharType> friend std::basic_ostream< CharType, std::char_traits<CharType> > & operator<<( std::basic_ostream< CharType, std::char_traits<CharType> > &os, TestClass const &t ) { return os << L"{age : " << t.age_ << L", name : " << t.name_ << L"}"; } }; int test_main(int argc, char **argv) { std::vector<std::string> strings; strings.push_back("hello"); strings.push_back("world"); strings.push_back("!"); std::list<TestClass> daughters; daughters.push_back(TestClass(14, L"空")); daughters.push_back(TestClass(10, L"美羽")); daughters.push_back(TestClass(3, L"ひな")); std::stringstream ss; std::wstringstream ws; ss << hwm::to_os << strings; //(0番目の要素, 1番目の要素, 2番目の要素, ...)という風に出力される。 BOOST_CHECK(ss.str() == "(hello, world, !)"); //windowsならワイド文字列のwcoutに必要になるかも。それと他の環境のことは知らない。 setlocale(LC_ALL, "japanese"); //wide文字列用のostreamにもそのまま使える。 std::wcout << L"ハッピハッピガー" << hwm::to_os << daughters << L"ハッピハッピガー" << std::endl; //ハッピハッピガー({age : 14, name : 空}, {age : 10, name : 美羽}, {age : 3, name : ひな})ハッピハッピガー return 0; }
hwm/to_ostream_outputtable.hpp
#ifndef HWM_TOOSTREAMOUTPUTTABLE_HPP #define HWM_TOOSTREAMOUTPUTTABLE_HPP #include <boost/range/iterator.hpp> #include <boost/range/begin.hpp> #include <boost/range/end.hpp> namespace hwm { namespace ostream_outputtable_detail { template<class CharType> struct ostream_output_impl { typedef std::basic_ostream< CharType, std::char_traits<CharType> > ostream_type; ostream_output_impl(ostream_type &os) : os_(os) {} template<class Container> ostream_type & operator<< (Container &ct) { typedef typename boost::range_iterator<Container>::type iterator; iterator it = boost::begin(ct); iterator const end = boost::end(ct); if(it == end) { os_ << "()"; } else { os_ << "("; for( ; ; ) { os_ << *it; ++it; if(it != end) { os_ << ", "; } else { os_ << ")"; break; } } } return os_; } ostream_type &os_; }; struct to_ostream_outputtable { template<class CharType> friend ostream_output_impl<CharType> operator<< ( std::basic_ostream< CharType, std::char_traits<CharType> > &os, to_ostream_outputtable const &t ) { return ostream_output_impl<CharType>(os); } }; } //namespace ostream_outputtable_detail static ostream_outputtable_detail::to_ostream_outputtable const to_os = {}; } //namespace hwm #endif //HWM_TOOSTREAMOUTPUTTABLE_HPP
ostreamによしなに出力したいだけなので、Pythonとかのタプルの形式で決め打ちしています
他の形式で出力するためにポリシークラスを用意したりするともっと便利になるかもですね、でも面倒なのでやってません。
直後に来るコンテナを出力したあとはただの元のostreamに戻るので、
複数のコンテナを出力するときは毎回コンテナの直前にhwm::to_osを置くようにします。
それから、本当はストリーム側じゃなくてコンテナ側をいじってostreamに出力可能なようにする方が行儀がいいのかも・・・?