libStatGen Software  1
STLUtilities.h
1 /*
2  * Copyright (C) 2010 Regents of the University of Michigan
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef _STLUTILITIES_H
19 #define _STLUTILITIES_H
20 #include <assert.h>
21 #include <iomanip>
22 #include <iostream>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <sstream>
26 #include <stdexcept>
27 #include <string>
28 #include <string.h>
29 #include <vector>
30 
31 
32 ///
33 /// This file is inspired by the poor quality of string support in
34 /// STL for what should be trivial capabiltiies, for example setting
35 /// or appending the ASCII representation of a floating point
36 /// or integer number to a string.
37 ///
38 /// This file uses variadic templates to implement a type safe
39 /// version (subset) of C-library printf.
40 ///
41 /// Therefore, -std=c++0x is a required option on g++
42 ///
43 
44 namespace STLUtilities
45 {
46 ///
47 /// use std streams API to do float conversion to string,
48 /// then append it.
49 ///
50 inline std::string &append(std::string &s, float f)
51 {
52  std::ostringstream buffer;
53  buffer << f;
54  s += buffer.str();
55  return s;
56 }
57 
58 ///
59 /// use std streams API to do double conversion to string,
60 /// then append it.
61 ///
62 inline std::string &append(std::string &s, double f)
63 {
64  std::ostringstream buffer;
65  buffer << f;
66  s += buffer.str();
67  return s;
68 }
69 
70 ///
71 /// The rest we can handle readily ourselves.
72 /// Unlike std::string operator +, this operator treats c as
73 /// a character and appends the ASCII character c.
74 ///
75 inline std::string &append(std::string &s, char c)
76 {
77  s += c;
78  return s;
79 }
80 
81 ///
82 /// Similar to signed char case, but this time for unsigned.
83 ///
84 inline std::string &append(std::string &s, unsigned char c)
85 {
86  s += c;
87  return s;
88 }
89 
90 ///
91 /// Now append a full C style NUL terminated string to
92 /// the std::string.
93 ///
94 inline std::string &append(std::string &s, const char *rhs)
95 {
96  s += rhs;
97  return s;
98 }
99 
100 ///
101 /// Prevent the generic template from picking up std::string
102 ///
103 inline std::string &append(std::string &s, std::string &rhs)
104 {
105  s += rhs;
106  return s;
107 }
108 
109 ///
110 /// iterate over the provided vector, appending all elements with
111 /// an optional separator
112 ///
113 template<typename T> std::string &append(std::string &s, std::vector<T> v, std::string separator="")
114 {
115  for (typename T::iterator i=v.begin(); i!=v.end(); i++)
116  {
117  if (i!=v.begin()) s += separator;
118  s << *i;
119  }
120  return s;
121 }
122 
123 ///
124 /// This template handles the rest of the cases for
125 /// integral types. Not user friendly if you pass
126 /// in a type T that is for example a std::vector.
127 ///
128 template<typename T> std::string &append(std::string &s, T i)
129 {
130  char digits[20];
131  char *p = digits;
132  bool negative = false;
133 
134  if (i<0)
135  {
136  negative = true;
137  i = -i;
138  }
139 
140  do
141  {
142  *p++ = '0' + i % 10;
143  i = i/10;
144  }
145  while (i);
146 
147  if (negative) s += '-';
148 
149  do
150  {
151  s += *--p;
152  }
153  while (p > digits);
154 
155  return s;
156 }
157 
158 inline std::string &operator <<(std::string &s, char c)
159 {
160  return append(s, c);
161 }
162 
163 inline std::string &operator <<(std::string &s, unsigned char c)
164 {
165  return append(s, c);
166 }
167 
168 inline std::string &operator <<(std::string &s, uint64_t i)
169 {
170  return append(s, i);
171 }
172 
173 inline std::string &operator <<(std::string &s, int64_t i)
174 {
175  return append(s, i);
176 }
177 
178 template<typename T> std::string &operator <<(std::string &s, T val)
179 {
180  return append(s, val);
181 }
182 
183 template<typename S> std::string &append(std::string &s, std::vector<std::string> v, S delimeter, bool itemize = false)
184 {
185  bool showDelimeter = false;
186  for (std::vector<std::string>::iterator i=v.begin(); i!=v.end(); i++)
187  {
188  if (showDelimeter) s << delimeter;
189  else showDelimeter = true;
190  if (itemize) s << (i - v.begin()) << ": ";
191  s << *i;
192  }
193  return s;
194 }
195 
196 template<typename T, typename S> std::string &append(std::string &s, std::vector<T> v, S delimeter, bool itemize = false)
197 {
198  bool showDelimeter = false;
199  for (typename std::vector<T>::iterator i=v.begin(); i!=v.end(); i++)
200  {
201  if (showDelimeter) s << delimeter;
202  else showDelimeter = true;
203  if (itemize) s << (i - v.begin()) << ": ";
204  s << *i;
205  }
206  return s;
207 }
208 
209 //
210 // Split the string input into words delimited by the character
211 // delimiter. For a given number of input delimiters, result.size()
212 // will not change, regardless of the data in between the delimiters.
213 //
214 // Refactor this to pre-allocate the word that we place data into,
215 // then we have minimal data copy.
216 //
217 int Tokenize(std::vector<std::string> &result, const char *input, char delimiter);
218 
219 
220 
221 
222 //
223 // Variadic templates necessary for reasonable printf implementation
224 // are only supported as an experimental feature that in theory is
225 // subject to changes in the future draft standard for C++.
226 //
227 // Only defined when the g++ option -std=c++0x is used.
228 //
229 //
230 #if defined(__GXX_EXPERIMENTAL_CXX0X__)
231 //
232 // problems in compatibility with stdio printf/fprintf:
233 // - variable field width (%*d) not implemented
234 // - L 0 fills to the left of the number through precision width
235 // (ie printf("%20.6d",12) yields 000012 in a 20 width field)
236 //
237 // What is different from C-style printf:
238 // type safe
239 // no truncation of type data
240 // no vairable width fields
241 //
242 
243 inline void fprintf(std::ostream &stream, const char* s)
244 {
245  while (*s)
246  {
247  if (*s == '%' && *++s != '%')
248  throw std::runtime_error("invalid format string: missing arguments");
249  stream << *s++;
250  }
251 }
252 
253 template<typename T, typename... Args>
254 void fprintf(std::ostream &stream, const char* s, const T& value, const Args&... args)
255 {
256  while (*s)
257  {
258  if (*s == '%' && *++s != '%')
259  {
260  bool leftJustify = false;
261  bool zeroPad = false;
262  int fieldWidth = 0;
263  int precision = 3;
264  char fillChar = ' ';
265  if (*s && *s == '-')
266  {
267  leftJustify = true;
268  s++;
269  }
270 
271  if (*s && *s == '0')
272  {
273  fillChar = '0';
274  zeroPad = true;
275  s++;
276  }
277 
278  while (*s && isdigit(*s))
279  {
280  fieldWidth *= 10;
281  fieldWidth += (*s - '0');
282  s++;
283  }
284 
285  if (*s && *s == '.')
286  {
287  precision = 0;
288  s++;
289  while (*s && isdigit(*s))
290  {
291  precision *= 10;
292  precision += (*s - '0');
293  s++;
294  }
295  s++;
296  }
297 
298  while (*s)
299  {
300  switch (*s)
301  {
302  case 's':
303  s++;
304  stream << std::setw(fieldWidth) << (leftJustify ? std::left : std::right) << value;
305  break;
306  case 'p':
307  case 'x':
308  case 'X':
309  s++;
310  stream << std::setw(fieldWidth) << std::setfill(fillChar) << (leftJustify ? std::left : std::right) << std::hex << value;
311  break;
312  case 'l':
313  case 'L':
314  s++;
315  continue;
316  case 'f':
317  case 'd':
318  case 'h':
319  case 'j':
320  case 't':
321  case 'z':
322  s++;
323  stream << std::setw(fieldWidth) << std::setfill(fillChar) << (leftJustify ? std::left : std::right) << std::dec << value;
324  break;
325  default:
326  throw std::runtime_error("Unrecognized printf conversion character");
327  break;
328  }
329  break;
330  }
331  fprintf(stream, s, args...);
332  return;
333  }
334  stream << *s++;
335  }
336  throw std::runtime_error("extra arguments provided to printf");
337 }
338 
339 template<typename T, typename... Args>
340 void printf(const char* s, const T& value, const Args&... args)
341 {
342  fprintf(std::cout, s, value, args...);
343 }
344 
345 template<typename... Args>
346 void sprintf(std::string &buffer, const char *fmt, const Args&... args)
347 {
348  std::ostringstream stream;
349 
350  fprintf((std::ostream &) stream, fmt, args...);
351 
352  // stream.str() returns a const std::string &, so we
353  // can't do a std::swap()
354  buffer = stream.str();
355 }
356 #endif
357 
358 
359 } // end namespace STLUtilities
360 
361 #endif
STLUtilities::append
std::string & append(std::string &s, float f)
use std streams API to do float conversion to string, then append it.
Definition: STLUtilities.h:50
STLUtilities
This file is inspired by the poor quality of string support in STL for what should be trivial capabil...
Definition: STLUtilities.cpp:20