TooN Algorithm Library - tag  0.2
printf.h
Go to the documentation of this file.
1 #ifndef TAG_PRINTF_H
2 #define TAG_PRINTF_H
3 
4 #include <iostream>
5 #include <iomanip>
6 #include <sstream>
7 #include <string>
8 #include <cctype>
9 
10 #include <tag/tuple.h>
11 
12 namespace tag
13 {
14  #ifndef DOXYGEN_IGNORE_INTERNAL
15  namespace Internal
16  {
17 
18  // Code for parsing and interpreting printf style format specifiers.
19  //Code for groking format strings
20  struct format
21  {
22  enum
23  {
24  ALT = 1, ZP=2, LEFT=4, SPACE=8, SIGN=16, PERCENT=32, BAD=64,
25  NO_PRECISION=-1, NO_WIDTH=-1
26  };
27 
28  int flags;
29  int width, precision;
30  char conversion;
31 
32  inline int parse(const std::string& fmt, int pos)
33  {
34  flags=0;
35  width=NO_WIDTH;
36  precision=NO_PRECISION;
37  conversion=0;
38 
39  int s = fmt.size();
40 
41  if(pos == s)
42  {
43  flags |= BAD;
44  return 0;
45  }
46 
47  //Check for literal
48  if(fmt[pos] == '%')
49  {
50  flags = PERCENT;
51  return pos+1;
52  }
53 
54  //Get flags (if any)
55  for(; pos < s; pos++)
56  {
57  char c = fmt[pos];
58 
59  if(c == '#')
60  flags |= ALT;
61  else if(c == '0')
62  flags |= ZP;
63  else if(c == ' ')
64  flags |= SPACE;
65  else if(c == '-')
66  flags |= LEFT;
67  else if(c == '+')
68  flags |= SIGN;
69  else
70  break;
71  }
72 
73  //Get width
74  for(; pos < s; pos++)
75  {
76  char c = fmt[pos];
77  if(isdigit(c))
78  if(width == NO_WIDTH)
79  width = c - '0';
80  else
81  width = width * 10 + (c-'0');
82  else
83  break;
84  }
85 
86  //Check for a precison
87  if(pos < s && fmt[pos] == '.')
88  {
89  precision = 0;
90  pos++;
91 
92  //Get width
93  for(; pos < s; pos++)
94  {
95  char c = fmt[pos];
96  if(isdigit(c))
97  precision = precision * 10 + (c-'0');
98  else
99  break;
100  }
101  }
102 
103  //Now, this should be the conversion
104  if(pos < s && isalpha(fmt[pos]))
105  conversion = fmt[pos++];
106  else
107  flags = BAD;
108 
109  return pos;
110  }
111  };
112 
113  //To make it looks like ostream << format works
114  template<class Char, class Traits> struct bound_format
115  {
116  bound_format(std::basic_ostream<Char, Traits>& os, const format& ff)
117  :o(os), f(ff)
118  {}
119 
120  std::basic_ostream<Char, Traits>& o;
121  const format& f;
122  };
123 
124 
125  template<class Char, class Traits> bound_format<Char, Traits> operator<<(std::basic_ostream<Char, Traits>& o, const format& f)
126  {
127  return bound_format<Char, Traits>(o, f);
128  }
129 
130  //Evaluate the result of osteram << format << X
131  template<class Char, class Traits, class C> std::basic_ostream<Char, Traits>& operator<<(bound_format<Char,Traits> f, const C& c)
132  {
133  using std::ios;
134  using namespace std;
135 
136  bool precision_is_max_width=0;
137 
138 
139  //Save old stream state.
140  int old_p = f.o.precision();
141  Char old_fill = f.o.fill();
142  ios::fmtflags old_flags = f.o.flags();
143 
144  //Conversion specific tricks
145  //Defaults
146  f.o.unsetf(ios::floatfield | ios::boolalpha);
147  f.o.setf(ios::dec);
148  f.o.fill(' ');
149 
150  //Process conversion characters. These can affect the formatting
151  //parameters below.
152  switch(f.f.conversion)
153  {
154  case 'f':
155  case 'F':
156  f.o.setf(ios::fixed);
157  break;
158 
159  case 'e':
160  case 'E':
161  f.o.setf(ios::scientific);
162  break;
163 
164  case 'g':
165  case 'G':
166  f.o.unsetf(ios::floatfield);
167  break;
168 
169  case 'x':
170  case 'X':
171  f.o << hex;
172  break;
173 
174  case 'o':
175  case 'O':
176  f.o << oct;
177  break;
178 
179  case 'b':
180  case 'B':
181  f.o << boolalpha;
182  break;
183 
184  case 's':
185  case 'S':
186  precision_is_max_width=1;
187  break;
188 
189  case 'k':
190  case 'K':
191  return f.o;
192  break;
193  }
194 
195 
196 
197  if(f.f.width != format::NO_WIDTH)
198  f.o.width(f.f.width);
199 
200  if(f.f.flags & format::ZP && !(f.f.flags & format::LEFT))
201  f.o.fill('0');
202 
203  if(f.f.flags & format::SIGN)
204  f.o.setf(ios::showpos);
205  else
206  f.o.unsetf(ios::showpos);
207 
208  if(f.f.flags & format::LEFT)
209  f.o.setf(ios::left);
210  else
211  f.o.setf(ios::internal);
212 
213  if(f.f.flags * format::ALT)
214  f.o.setf(ios::showbase | ios::showpoint);
215  else
216  f.o.unsetf(ios::showbase | ios::showpoint);
217 
218 
219  if(isupper(f.f.conversion))
220  f.o.setf(ios::uppercase);
221  else
222  f.o.unsetf(ios::uppercase);
223 
224 
225  if(f.f.precision != format::NO_PRECISION && ! precision_is_max_width)
226  f.o.precision(f.f.precision);
227 
228  if(precision_is_max_width && f.f.precision != format::NO_PRECISION)
229  {
230  ostringstream tmp;
231  tmp.copyfmt(f.o);
232 
233  //Since we're doing the truncation by hand, then there should be
234  //no width specification
235  tmp << setw(0) << c;
236 
237  f.o << tmp.str().substr(0, f.f.precision);
238  }
239  else
240  f.o << c;
241 
242 
243  //Reset values
244  f.o.precision(old_p);
245  f.o.fill(old_fill);
246  f.o.setf(old_flags);
247 
248  return f.o;
249  }
250 
251  //This parses a format tring and uses it to print a typelist. The typelist has to be
252  //accessed in reverse order, since the last element added to the typelist is the
253  //one at the start of the list. Since lists are produced with (Fmt, a, b, c), that means
254  //that c would be accessed first. To make it look like printf, a needs to be accessed first.
255  template<class C, class D, int i, int max> struct print_typelist
256  {
257  static void print(std::ostream& o, const std::string& fmt, int fpos, const T_list<C,D>& l)
258  {
259  size_t ppos;
260 
261  while(1)
262  {
263  ppos = fmt.find('%', fpos);
264 
265  if(ppos == fmt.npos)
266  {
267  //No more format specifiers, so output the rest of the string
268  o << &fmt[fpos];
269  return;
270  }
271 
272  //else output the strung up to the specifier
273  o << fmt.substr(fpos, ppos - fpos);
274 
275  //Parse the format string
276  format f;
277  int pos = f.parse(fmt, ppos+1);
278 
279  if(f.flags & format::PERCENT)
280  {
281  o << '%';
282  fpos = pos;
283  continue;
284  }
285  else if(f.flags & format::BAD)
286  {
287  o << "<Malformed format>" << &fmt[ppos];
288  return ;
289  }
290  else
291  {
292  o << f << l.template index<i>();
293  print_typelist<C,D, i+1, max>::print(o, fmt, pos, l);
294  return;
295  }
296  }
297  }
298 
299  };
300 
301  template<class C, class D, int max> struct print_typelist<C, D, max, max>
302  {
303  static void print(std::ostream& o, const std::string& fmt, int fpos, const T_list<C,D>&)
304  {
305  size_t ppos;
306 
307  while(1)
308  {
309  ppos = fmt.find('%', fpos);
310 
311  if(ppos == fmt.npos)
312  {
313  //No more format specifiers, so output the rest of the string
314  o << &fmt[fpos];
315  return;
316  }
317 
318  //else output the strung up to the specifier
319  o << fmt.substr(fpos, ppos - fpos);
320 
321  //Parse the format string
322  format f;
323  int pos = f.parse(fmt, ppos+1);
324 
325  if(f.flags & format::PERCENT)
326  {
327  o << '%';
328  fpos = pos;
329  continue;
330  }
331  else if(f.flags & format::BAD)
332  {
333  o << "<Malformed format>" << &fmt[ppos];
334  return ;
335  }
336  else
337  {
338  o << "<Missing value>";
339  fpos = pos;
340  continue;
341  }
342  }
343  }
344  };
345  }
346  #endif
347 
348 
353 
441  template<class A, class B, class C, class D> void vfPrintf(std::basic_ostream<A, B>& o, const std::string fmt, const T_list<C,D>& l)
442  {
443  Internal::print_typelist<C, D, 0, T_list<C,D>::elements >::print(o, fmt, 0, l);
444  }
445 
450  template<class C, class D> void vPrintf(const std::string fmt, const T_list<C,D>& l)
451  {
452  vfPrintf(std::cout, fmt, l);
453  }
454 
460  template<class C, class D> std::string vsPrintf(const std::string& fmt, const T_list<C,D>& l)
461  {
462  std::ostringstream o;
463  vfPrintf(o, fmt, l);
464  return o.str();
465  }
466 
475  static const T_ListEnd Fmt=TupleHead;
476 
477  //Old cvs version has by-hand variable argument list up to 10 args
478 }
479 
485 #define Printf(A, ...) vPrintf(A, (tag::Fmt,## __VA_ARGS__))
486 
493 #define fPrintf(A,B, ...) vfPrintf(A,B, (tag::Fmt,## __VA_ARGS__))
494 
501 #define sPrintf(A, ...) vsPrintf(A, (tag::Fmt,## __VA_ARGS__))
502 #endif