libStatGen Software
1
|
00001 /* 00002 * Copyright (C) 2010 Regents of the University of Michigan 00003 * 00004 * This program is free software: you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation, either version 3 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 #ifndef _STLUTILITIES_H 00019 #define _STLUTILITIES_H 00020 #include <assert.h> 00021 #include <iomanip> 00022 #include <iostream> 00023 #include <stdint.h> 00024 #include <stdlib.h> 00025 #include <sstream> 00026 #include <stdexcept> 00027 #include <string> 00028 #include <string.h> 00029 #include <vector> 00030 00031 00032 /// 00033 /// This file is inspired by the poor quality of string support in 00034 /// STL for what should be trivial capabiltiies, for example setting 00035 /// or appending the ASCII representation of a floating point 00036 /// or integer number to a string. 00037 /// 00038 /// This file uses variadic templates to implement a type safe 00039 /// version (subset) of C-library printf. 00040 /// 00041 /// Therefore, -std=c++0x is a required option on g++ 00042 /// 00043 00044 namespace STLUtilities 00045 { 00046 /// 00047 /// use std streams API to do float conversion to string, 00048 /// then append it. 00049 /// 00050 inline std::string &append(std::string &s, float f) 00051 { 00052 std::ostringstream buffer; 00053 buffer << f; 00054 s += buffer.str(); 00055 return s; 00056 } 00057 00058 /// 00059 /// use std streams API to do double conversion to string, 00060 /// then append it. 00061 /// 00062 inline std::string &append(std::string &s, double f) 00063 { 00064 std::ostringstream buffer; 00065 buffer << f; 00066 s += buffer.str(); 00067 return s; 00068 } 00069 00070 /// 00071 /// The rest we can handle readily ourselves. 00072 /// Unlike std::string operator +, this operator treats c as 00073 /// a character and appends the ASCII character c. 00074 /// 00075 inline std::string &append(std::string &s, char c) 00076 { 00077 s += c; 00078 return s; 00079 } 00080 00081 /// 00082 /// Similar to signed char case, but this time for unsigned. 00083 /// 00084 inline std::string &append(std::string &s, unsigned char c) 00085 { 00086 s += c; 00087 return s; 00088 } 00089 00090 /// 00091 /// Now append a full C style NUL terminated string to 00092 /// the std::string. 00093 /// 00094 inline std::string &append(std::string &s, const char *rhs) 00095 { 00096 s += rhs; 00097 return s; 00098 } 00099 00100 /// 00101 /// Prevent the generic template from picking up std::string 00102 /// 00103 inline std::string &append(std::string &s, std::string &rhs) 00104 { 00105 s += rhs; 00106 return s; 00107 } 00108 00109 /// 00110 /// iterate over the provided vector, appending all elements with 00111 /// an optional separator 00112 /// 00113 template<typename T> std::string &append(std::string &s, std::vector<T> v, std::string separator="") 00114 { 00115 for (typename T::iterator i=v.begin(); i!=v.end(); i++) 00116 { 00117 if (i!=v.begin()) s += separator; 00118 s << *i; 00119 } 00120 return s; 00121 } 00122 00123 /// 00124 /// This template handles the rest of the cases for 00125 /// integral types. Not user friendly if you pass 00126 /// in a type T that is for example a std::vector. 00127 /// 00128 template<typename T> std::string &append(std::string &s, T i) 00129 { 00130 char digits[20]; 00131 char *p = digits; 00132 bool negative = false; 00133 00134 if (i<0) 00135 { 00136 negative = true; 00137 i = -i; 00138 } 00139 00140 do 00141 { 00142 *p++ = '0' + i % 10; 00143 i = i/10; 00144 } 00145 while (i); 00146 00147 if (negative) s += '-'; 00148 00149 do 00150 { 00151 s += *--p; 00152 } 00153 while (p > digits); 00154 00155 return s; 00156 } 00157 00158 inline std::string &operator <<(std::string &s, char c) 00159 { 00160 return append(s, c); 00161 } 00162 00163 inline std::string &operator <<(std::string &s, unsigned char c) 00164 { 00165 return append(s, c); 00166 } 00167 00168 inline std::string &operator <<(std::string &s, uint64_t i) 00169 { 00170 return append(s, i); 00171 } 00172 00173 inline std::string &operator <<(std::string &s, int64_t i) 00174 { 00175 return append(s, i); 00176 } 00177 00178 template<typename T> std::string &operator <<(std::string &s, T val) 00179 { 00180 return append(s, val); 00181 } 00182 00183 template<typename S> std::string &append(std::string &s, std::vector<std::string> v, S delimeter, bool itemize = false) 00184 { 00185 bool showDelimeter = false; 00186 for (std::vector<std::string>::iterator i=v.begin(); i!=v.end(); i++) 00187 { 00188 if (showDelimeter) s << delimeter; 00189 else showDelimeter = true; 00190 if (itemize) s << (i - v.begin()) << ": "; 00191 s << *i; 00192 } 00193 return s; 00194 } 00195 00196 template<typename T, typename S> std::string &append(std::string &s, std::vector<T> v, S delimeter, bool itemize = false) 00197 { 00198 bool showDelimeter = false; 00199 for (typename std::vector<T>::iterator i=v.begin(); i!=v.end(); i++) 00200 { 00201 if (showDelimeter) s << delimeter; 00202 else showDelimeter = true; 00203 if (itemize) s << (i - v.begin()) << ": "; 00204 s << *i; 00205 } 00206 return s; 00207 } 00208 00209 // 00210 // Split the string input into words delimited by the character 00211 // delimiter. For a given number of input delimiters, result.size() 00212 // will not change, regardless of the data in between the delimiters. 00213 // 00214 // Refactor this to pre-allocate the word that we place data into, 00215 // then we have minimal data copy. 00216 // 00217 int Tokenize(std::vector<std::string> &result, const char *input, char delimiter); 00218 00219 00220 00221 00222 // 00223 // Variadic templates necessary for reasonable printf implementation 00224 // are only supported as an experimental feature that in theory is 00225 // subject to changes in the future draft standard for C++. 00226 // 00227 // Only defined when the g++ option -std=c++0x is used. 00228 // 00229 // 00230 #if defined(__GXX_EXPERIMENTAL_CXX0X__) 00231 // 00232 // problems in compatibility with stdio printf/fprintf: 00233 // - variable field width (%*d) not implemented 00234 // - L 0 fills to the left of the number through precision width 00235 // (ie printf("%20.6d",12) yields 000012 in a 20 width field) 00236 // 00237 // What is different from C-style printf: 00238 // type safe 00239 // no truncation of type data 00240 // no vairable width fields 00241 // 00242 00243 inline void fprintf(std::ostream &stream, const char* s) 00244 { 00245 while (*s) 00246 { 00247 if (*s == '%' && *++s != '%') 00248 throw std::runtime_error("invalid format string: missing arguments"); 00249 stream << *s++; 00250 } 00251 } 00252 00253 template<typename T, typename... Args> 00254 void fprintf(std::ostream &stream, const char* s, const T& value, const Args&... args) 00255 { 00256 while (*s) 00257 { 00258 if (*s == '%' && *++s != '%') 00259 { 00260 bool leftJustify = false; 00261 bool zeroPad = false; 00262 int fieldWidth = 0; 00263 int precision = 3; 00264 char fillChar = ' '; 00265 if (*s && *s == '-') 00266 { 00267 leftJustify = true; 00268 s++; 00269 } 00270 00271 if (*s && *s == '0') 00272 { 00273 fillChar = '0'; 00274 zeroPad = true; 00275 s++; 00276 } 00277 00278 while (*s && isdigit(*s)) 00279 { 00280 fieldWidth *= 10; 00281 fieldWidth += (*s - '0'); 00282 s++; 00283 } 00284 00285 if (*s && *s == '.') 00286 { 00287 precision = 0; 00288 s++; 00289 while (*s && isdigit(*s)) 00290 { 00291 precision *= 10; 00292 precision += (*s - '0'); 00293 s++; 00294 } 00295 s++; 00296 } 00297 00298 while (*s) 00299 { 00300 switch (*s) 00301 { 00302 case 's': 00303 s++; 00304 stream << std::setw(fieldWidth) << (leftJustify ? std::left : std::right) << value; 00305 break; 00306 case 'p': 00307 case 'x': 00308 case 'X': 00309 s++; 00310 stream << std::setw(fieldWidth) << std::setfill(fillChar) << (leftJustify ? std::left : std::right) << std::hex << value; 00311 break; 00312 case 'l': 00313 case 'L': 00314 s++; 00315 continue; 00316 case 'f': 00317 case 'd': 00318 case 'h': 00319 case 'j': 00320 case 't': 00321 case 'z': 00322 s++; 00323 stream << std::setw(fieldWidth) << std::setfill(fillChar) << (leftJustify ? std::left : std::right) << std::dec << value; 00324 break; 00325 default: 00326 throw std::runtime_error("Unrecognized printf conversion character"); 00327 break; 00328 } 00329 break; 00330 } 00331 fprintf(stream, s, args...); 00332 return; 00333 } 00334 stream << *s++; 00335 } 00336 throw std::runtime_error("extra arguments provided to printf"); 00337 } 00338 00339 template<typename T, typename... Args> 00340 void printf(const char* s, const T& value, const Args&... args) 00341 { 00342 fprintf(std::cout, s, value, args...); 00343 } 00344 00345 template<typename... Args> 00346 void sprintf(std::string &buffer, const char *fmt, const Args&... args) 00347 { 00348 std::ostringstream stream; 00349 00350 fprintf((std::ostream &) stream, fmt, args...); 00351 00352 // stream.str() returns a const std::string &, so we 00353 // can't do a std::swap() 00354 buffer = stream.str(); 00355 } 00356 #endif 00357 00358 00359 } // end namespace STLUtilities 00360 00361 #endif