ネストされた range の入出力
実用には耐えませんが、ちょっとしたデバッグなどに。
入力をオウム返しする例
#include <deque> #include <iostream> #include <list> #include <vector> #include "./printed.hpp" #include "./scanned.hpp" int main() { std::deque<std::list<std::vector<int>>> rng; std::cin >> (rng | vitro::scanned); // >>> {{{},{1}},{{2,3},{4,5,6}}} std::cout << (rng | vitro::printed); // {{{},{1}},{{2,3},{4,5,6}}} }
VC10 と GCC 4.5.2 (-std=c++0x) で動きます。入力を受ける range は operator >> をオーバーロードしているか、sequence container でなければなりません。
実装は以下から。SFINAE は偉大です。
scanned.hpp
#ifndef VITRO_RANGE_SCANNED_HPP #define VITRO_RANGE_SCANNED_HPP #include <ios> #include <istream> #include <utility> #include <boost/iterator/iterator_categories.hpp> #include <boost/iterator/iterator_facade.hpp> #include <boost/range/value_type.hpp> #include <boost/swap.hpp> namespace vitro { namespace range_detail { template <class IStream> inline IStream &eat(IStream &s, char c) { typename IStream::char_type val; if (s >> val && val != s.widen(c)) { s.putback(val); s.setstate(std::ios::failbit); } return s; } template <class T> struct scan_holder { scan_holder(T &val) : value(val) {} T &value; }; template <class T, class Char, class Traits> class scan_iterator : public boost::iterator_facade< scan_iterator<T, Char, Traits>, T, boost::single_pass_traversal_tag, T && > { public: typedef std::basic_istream<Char, Traits> istream_type; scan_iterator() : stream(0) {} scan_iterator(istream_type &s) : stream(&s), first(true) { read(); } private: friend class boost::iterator_core_access; T &&dereference() const { return std::move(value); } bool equal(scan_iterator const &r) const { return (stream && *stream) == (r.stream && *r.stream); } void increment() { read(); } void read() { if (first) { first = false; *stream >> scan_holder<T>(value); } else if (eat(*stream, ',')) *stream >> scan_holder<T>(value); } istream_type *stream; mutable T value; bool first; }; template <class IStream, class T> inline IStream &in(IStream &s, scan_holder<T> h, ...) { typedef scan_iterator< typename boost::range_value<T>::type, typename IStream::char_type, typename IStream::traits_type > iterator_type; if (eat(s, '{')) { T val((iterator_type(s)), iterator_type()); s.clear(); if (eat(s, '}')) boost::swap(h.value, val); } return s; } template <class IStream, class T> inline auto in(IStream &s, scan_holder<T> h, int) -> decltype(s >> h.value) { return s >> h.value; } template <class Char, class Traits, class T> inline std::basic_istream<Char, Traits> & operator >>(std::basic_istream<Char, Traits> &s, scan_holder<T> h) { return in(s, h, 0); } struct scan_forwarder {}; template <class T> inline scan_holder<T> operator |(T &val, scan_forwarder) { return scan_holder<T>(val); } } // namespace range_detail namespace { range_detail::scan_forwarder const scanned = {}; } // anonymous-namespace } // namespace vitro #endif
printed.hpp
#ifndef VITRO_RANGE_PRINTED_HPP #define VITRO_RANGE_PRINTED_HPP #include <ostream> #include <boost/range/begin.hpp> #include <boost/range/end.hpp> #include <boost/range/value_type.hpp> #include <boost/type_traits/is_array.hpp> #include <boost/utility/enable_if.hpp> namespace vitro { namespace range_detail { template <class T> struct print_holder { print_holder(T const &val) : value(val) {} T const &value; }; template <class OStream, class T> inline OStream &out(OStream &s, print_holder<T> h, ...) { s << s.widen('{'); bool first = true; auto end = boost::end(h.value); for (auto it = boost::begin(h.value); it != end; ++it) { if (first) first = false; else s << s.widen(','); s << print_holder<typename boost::range_value<T>::type>(*it); } s << s.widen('}'); return s; } template <class OStream, class T> inline auto out(OStream &s, print_holder<T> h, typename boost::disable_if<boost::is_array<T>, int>::type) -> decltype(s << h.value) { return s << h.value; } template <class Char, class Traits, class T> inline std::basic_ostream<Char, Traits> & operator <<(std::basic_ostream<Char, Traits> &s, print_holder<T> h) { return out(s, h, 0); } struct print_forwarder {}; template <class T> inline print_holder<T> operator |(T const &val, print_forwarder) { return print_holder<T>(val); } } // namespace range_detail namespace { range_detail::print_forwarder const printed = {}; } // anonymous-namespace } // namespace vitro #endif
boost::iterator_facade は reference が T && でも動くんだ、という変なところで感心しました。