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
44namespace STLUtilities
45{
46///
47/// use std streams API to do float conversion to string,
48/// then append it.
49///
50inline 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///
62inline 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///
75inline 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///
84inline 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///
94inline 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///
103inline 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///
113template<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///
128template<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
158inline std::string &operator <<(std::string &s, char c)
159{
160 return append(s, c);
161}
162
163inline std::string &operator <<(std::string &s, unsigned char c)
164{
165 return append(s, c);
166}
167
168inline std::string &operator <<(std::string &s, uint64_t i)
169{
170 return append(s, i);
171}
172
173inline std::string &operator <<(std::string &s, int64_t i)
174{
175 return append(s, i);
176}
177
178template<typename T> std::string &operator <<(std::string &s, T val)
179{
180 return append(s, val);
181}
182
183template<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
196template<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//
217int 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
243inline 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
253template<typename T, typename... Args>
254void 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
339template<typename T, typename... Args>
340void printf(const char* s, const T& value, const Args&... args)
341{
342 fprintf(std::cout, s, value, args...);
343}
344
345template<typename... Args>
346void 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
This file is inspired by the poor quality of string support in STL for what should be trivial capabil...
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