1 // Boost.TypeErasure library
2 //
3 // Copyright 2011 Steven Watanabe
4 //
5 // Distributed under the Boost Software License Version 1.0. (See
6 // accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // $Id$
10 
11 //[print_sequence
12 
13 /*`
14     (For the source of this example see
15     [@boost:/libs/type_erasure/example/print_sequence.cpp print_sequence.cpp])
16 
17     This example defines a class hierarchy that allows a sequence
18     to be formatted in several different ways.  We'd like to be
19     able to handle any sequence and any stream type, since the
20     range formatting is independent of the formatting of
21     individual elements.  Thus, our interface needs to look
22     something like this:
23 
24     ``
25         class abstract_printer {
26         public:
27             template<class CharT, class Traits, class Range>
28             virtual void print(std::basic_ostream<CharT, Traits>& os, const Range& r) const = 0;
29         };
30     ``
31 
32     Unfortunately, this is illegal because a virtual function
33     cannot be a template.  However, we can define a
34     class with much the same behavior using Boost.TypeErasure.
35 */
36 
37 #include <boost/type_erasure/any.hpp>
38 #include <boost/type_erasure/iterator.hpp>
39 #include <boost/type_erasure/operators.hpp>
40 #include <boost/type_erasure/tuple.hpp>
41 #include <boost/type_erasure/same_type.hpp>
42 #include <boost/range/begin.hpp>
43 #include <boost/range/end.hpp>
44 #include <boost/range/iterator.hpp>
45 #include <iostream>
46 #include <iomanip>
47 #include <vector>
48 
49 using namespace boost::type_erasure;
50 
51 struct _t : placeholder {};
52 struct _iter : placeholder {};
53 struct _os : placeholder {};
54 
55 template<class T, class U = _self>
56 struct base_and_derived
57 {
applybase_and_derived58     static T& apply(U& arg) { return arg; }
59 };
60 
61 namespace boost {
62 namespace type_erasure {
63 
64 template<class T, class U, class Base>
65 struct concept_interface<base_and_derived<T, U>, Base, U> : Base
66 {
operator typename rebind_any<Base,const T&>::typeboost::type_erasure::concept_interface67     operator typename rebind_any<Base, const T&>::type() const
68     {
69         return call(base_and_derived<T, U>(), const_cast<concept_interface&>(*this));
70     }
operator typename rebind_any<Base,T&>::typeboost::type_erasure::concept_interface71     operator typename rebind_any<Base, T&>::type()
72     {
73         return call(base_and_derived<T, U>(), *this);
74     }
75 };
76 
77 }
78 }
79 
80 // abstract_printer - An abstract base class for formatting sequences.
81 class abstract_printer {
82 public:
83     // print - write a sequence to a std::ostream in a manner
84     //   specific to the derived class.
85     //
86     // Requires: Range must be a Forward Range whose elements can be
87     //   printed to os.
88     template<class CharT, class Traits, class Range>
print(std::basic_ostream<CharT,Traits> & os,const Range & r) const89     void print(std::basic_ostream<CharT, Traits>& os, const Range& r) const {
90         // Capture the arguments
91         typename boost::range_iterator<const Range>::type
92             first(boost::begin(r)),
93             last(boost::end(r));
94         tuple<requirements, _os&, _iter, _iter> args(os, first, last);
95         // and forward to the real implementation
96         do_print(get<0>(args), get<1>(args), get<2>(args));
97     }
~abstract_printer()98     virtual ~abstract_printer() {}
99 protected:
100     // define the concept requirements of the arguments of
101     // print and typedef the any types.
102     typedef boost::mpl::vector<
103         base_and_derived<std::ios_base, _os>,
104         ostreamable<_os, _t>,
105         ostreamable<_os, const char*>,
106         forward_iterator<_iter, const _t&>,
107         same_type<_t, forward_iterator<_iter, const _t&>::value_type>
108     > requirements;
109     typedef boost::type_erasure::any<requirements, _os&> ostream_type;
110     typedef boost::type_erasure::any<requirements, _iter> iterator_type;
111     // do_print - This method must be implemented by derived classes
112     virtual void do_print(
113         ostream_type os, iterator_type first, iterator_type last) const = 0;
114 };
115 
116 // separator_printer - writes the elements of a sequence
117 //   separated by a fixed string.  For example, if
118 //   the separator is ", " separator_printer produces
119 //   a comma separated list.
120 class separator_printer : public abstract_printer {
121 public:
separator_printer(const std::string & sep)122     explicit separator_printer(const std::string& sep) : separator(sep) {}
123 protected:
do_print(ostream_type os,iterator_type first,iterator_type last) const124     virtual void do_print(
125         ostream_type os, iterator_type first, iterator_type last) const {
126         if(first != last) {
127             os << *first;
128             ++first;
129             for(; first != last; ++first) {
130                 os << separator.c_str() << *first;
131             }
132         }
133     }
134 private:
135     std::string separator;
136 };
137 
138 // column_separator_printer - like separator_printer, but
139 //   also inserts a line break after every n elements.
140 class column_separator_printer : public abstract_printer {
141 public:
column_separator_printer(const std::string & sep,std::size_t num_columns)142     column_separator_printer(const std::string& sep, std::size_t num_columns)
143       : separator(sep),
144         cols(num_columns)
145     {}
146 protected:
do_print(ostream_type os,iterator_type first,iterator_type last) const147     virtual void do_print(
148         ostream_type os, iterator_type first, iterator_type last) const {
149         std::size_t count = 0;
150         for(; first != last; ++first) {
151             os << *first;
152             boost::type_erasure::any<requirements, _iter> temp = first;
153             ++temp;
154             if(temp != last) {
155                 os << separator.c_str();
156             }
157             if(++count % cols == 0) {
158                 os << "\n";
159             }
160         }
161     }
162 private:
163     std::string separator;
164     std::size_t cols;
165 };
166 
167 // aligned_column_printer - formats a sequence in columns
168 //   reading down.  For example, given the sequence
169 //   { 1, 2, 3, 4, 5 }, aligned_column_printer might print
170 //   1   4
171 //   2   5
172 //   3
173 class aligned_column_printer : public abstract_printer {
174 public:
aligned_column_printer(std::size_t column_width,std::size_t num_columns)175     aligned_column_printer(std::size_t column_width, std::size_t num_columns)
176       : width(column_width),
177         cols(num_columns)
178     {}
179 protected:
do_print(ostream_type os,iterator_type first,iterator_type last) const180     virtual void do_print(
181         ostream_type os, iterator_type first, iterator_type last) const
182     {
183         if(first == last) return;
184         std::vector<iterator_type> column_iterators;
185 
186         // find the tops of the columns
187         std::size_t count = 0;
188         for(iterator_type iter = first; iter != last; ++iter) {
189             ++count;
190         }
191         std::size_t rows = (count + cols - 1) / cols;
192         count = 0;
193         for(iterator_type iter = first; iter != last; ++iter) {
194             if(count % rows == 0) {
195                 column_iterators.push_back(iter);
196             }
197             ++count;
198         }
199 
200         iterator_type last_col = column_iterators.back();
201 
202         // print the full rows
203         while(column_iterators.back() != last) {
204             for(std::vector<iterator_type>::iterator
205                 iter = column_iterators.begin(),
206                 end = column_iterators.end(); iter != end; ++iter)
207             {
208                 static_cast<std::ios_base&>(os).width(width);
209                 os << **iter;
210                 ++*iter;
211             }
212             os << "\n";
213         }
214 
215         // print the rows that are missing the last column
216         column_iterators.pop_back();
217         if(!column_iterators.empty()) {
218             while(column_iterators.back() != last_col) {
219                 for(std::vector<iterator_type>::iterator
220                     iter = column_iterators.begin(),
221                     end = column_iterators.end(); iter != end; ++iter)
222                 {
223                     static_cast<std::ios_base&>(os).width(width);
224                     os << **iter;
225                     ++*iter;
226                 }
227                 os << "\n";
228             }
229         }
230     }
231 private:
232     std::size_t width;
233     std::size_t cols;
234 };
235 
main()236 int main() {
237     int test[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
238     separator_printer p1(",");
239     p1.print(std::cout, test);
240     std::cout << std::endl;
241     column_separator_printer p2(",", 4);
242     p2.print(std::cout, test);
243     std::cout << std::endl;
244     aligned_column_printer p3(16, 4);
245     p3.print(std::cout, test);
246 }
247 
248 //]
249