All Classes Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
fprint.hpp
1 // The MIT License (MIT)
2 
3 // Copyright (c) 2012-2014 Danny Y., Rapptz
4 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal in
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 // the Software, and to permit persons to whom the Software is furnished to do so,
10 // subject to the following conditions:
11 
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #ifndef GEARS_IO_FPRINT_HPP
23 #define GEARS_IO_FPRINT_HPP
24 
25 #include "detail/index_printer.hpp"
26 #include "../string/classification.hpp"
27 #include <string>
28 
29 namespace gears {
30 namespace io {
31 
113 template<class Elem, class Traits, typename... Args>
114 inline void fprint(std::basic_ostream<Elem, Traits>& out, const std::basic_string<Elem, Traits>& str, Args&&... arguments) {
115  if(sizeof...(arguments) < 1) {
116  out << str;
117  return;
118  }
119 
120  auto args = std::make_tuple(std::forward<Args>(arguments)...);
121 
122  string::is_digit cmp;
123  auto&& length = str.size();
124  auto&& original_width = out.width();
125  std::ios_base::fmtflags original_format = out.flags();
126  auto&& original_precision = out.precision();
127 
128  for(decltype(str.size()) i = 0; i < length; ++i) {
129  auto&& c = str[i];
130  // doesn't start with { so just print it and continue
131  if(c != out.widen('{')) {
132  out << c;
133  continue;
134  }
135 
136  // at this point, the character c points to {
137  // check if we're done printing
138  if(i + 1 > length) {
139  out << c;
140  break;
141  }
142 
143  // check the next characters
144  auto j = i + 1;
145  unsigned index = 0;
146  decltype(out.width()) width = 0;
147  decltype(out.precision()) precision = 0;
148  auto format = original_format;
149 
150  // escaped character
151  if(str[j] == out.widen('{')) {
152  out << str[i];
153  i = j;
154  continue;
155  }
156 
157  // now we're at a sane point where we can work with the format string
158  // check if the next character is a digit
159  if(cmp(str[j])) {
160  do {
161  // since it is, multiply the index
162  index = (index * 10) + (str[j++] - out.widen('0'));
163  }
164  while(j < length && cmp(str[j]));
165  }
166  else {
167  // since it isn't a digit, it doesn't match our format string
168  throw std::runtime_error("invalid format string specified");
169  }
170 
171  // check if alignment argument exists
172  if(str[j] == out.widen(',')) {
173  // check if the next character is valid
174  if(j + 1 < length) {
175  // check if the alignment is left or right
176  if(str[j + 1] == out.widen('-')) {
177  format |= out.left;
178  // increment by two to get to the numerical section
179  j += 2;
180  }
181  else {
182  format |= out.right;
183  ++j;
184  }
185  // check if the next character is a digit
186  if(j < length && cmp(str[j])) {
187  do {
188  // since it is, multiply the width
189  width = (width * 10) + (str[j++] - out.widen('0'));
190  }
191  while(j < length && cmp(str[j]));
192  }
193  else {
194  // invalid format string found
195  throw std::runtime_error("invalid format string specified");
196  }
197 
198  }
199  }
200 
201  // check if format specifier exists
202  if(str[j] == out.widen(':')) {
203  // check if the character is valid
204  if(j + 1 < length) {
205  auto&& specifier = str[j + 1];
206  switch(specifier) {
207  case 'F':
208  format |= out.fixed;
209  break;
210  case 'O':
211  format = (format & ~out.basefield) | out.oct;
212  break;
213  case 'x':
214  format = (format & ~out.basefield) | out.hex;
215  break;
216  case 'X':
217  format = (format & ~out.basefield) | out.hex | out.uppercase;
218  break;
219  case 'E':
220  format |= out.scientific | out.uppercase;
221  break;
222  case 'e':
223  format |= out.scientific;
224  break;
225  case 'B':
226  format |= out.boolalpha;
227  break;
228  case 'S':
229  format |= out.showpos;
230  break;
231  default:
232  throw std::runtime_error("no such format specifier found");
233  break;
234  }
235  j += 2;
236 
237  // handle precision specifier
238  if(j < length && cmp(str[j])) {
239  do {
240  precision = (precision * 10) + (str[j++] - out.widen('0'));
241  }
242  while(j < length && cmp(str[j]));
243  }
244  }
245  }
246 
247  // now that we're done processing, handle the results
248  if(str[j] == out.widen('}')) {
249  out.flags(format);
250  out.width(width);
251  out.precision(precision);
252  detail::index_printer(out, index, args);
253  out.width(original_width);
254  out.flags(original_format);
255  out.precision(original_precision);
256  i = j;
257  }
258  }
259 }
260 } // io
261 } // gears
262 
263 #endif // GEARS_IO_FPRINT_HPP