1 // --------------------------------------------------------------------------- 2 // Utilities for use with Scott Meyers' "Effective STL" Course. 3 // Copyright 2000 by Scott Meyers. 4 // 5 // Last modified 3/27/01. 6 // 7 // This file offers four basic utilities: 8 // - A preprocessor symbol, MSVC, that's defined only when compiling with 9 // MSVC. 10 // - Functions to help fill containers with seqences of values. Search 11 // for "makeSequence". 12 // - Functions to print the contents of STL containers. Search for 13 // "printContainer" 14 // - A class to time how long it takes to do something. Search for 15 // "Timer". 16 // Note that everything in this file except MSVC is in the namespace 17 // ESTLUtils. (Macros and preprocessor symbols never respect namespace 18 // boundaries.) 19 // 20 // Platforms on which this header has so far been tested: 21 // Borland C++ 5.5, native library, WinNT 4.0 22 // MetroWerks CodeWarrior Pro 5.3, native library, WinNT 4.0 or 23 // MetroWerks CodeWarrior Pro 6.0, native library, Windows 2000 24 // Microsoft Visual C++ 6.0 (SP3), native library, WinNT 4.0 25 // Gnu gcc 2.95.2 (mingw32 distribution), native library, WinNT 4.0 26 // Comeau C++ 4.2.45.2, native library, Windows 2000 27 // 28 // All functions are inline, because otherwise MSVC complains about 29 // multiply defined symbols when linking projects made up of multiple 30 // source files, at least two of which #include this file. 31 // --------------------------------------------------------------------------- 32 #ifndef ESTLUTILS_H 33 #define ESTLUTILS_H 34 35 #include <algorithm> 36 #include <functional> 37 #include <iostream> 38 #include <string> 39 #include <ctime> 40 #include "InitUtil.h" 41 42 // --------------------------------------------------------------------------- 43 // MSVC6, when used in conjunction with the library that ships with the 44 // compiler, has some limitations not present in most other compilers 45 // (e.g., it lacks member templates), so it's convenient to know when we're 46 // compiling with MSVC6. The following figures that out. (In theory, all 47 // we need to do is look for the preprocessor symbol "_MSC_VER"), but other 48 // compilers for Windows define that, too, so we need to rule the other 49 // compilers out before concluding that we've got MSVC.) 50 // 51 // If you're using MSVC6, but you're not using the library that ships with 52 // the compiler, much of the conditional compilation for MSVC may become 53 // unnecessary. MSVC6 does have limitations, but it is a much more capable 54 // compiler than the library shipping with it suggests. 55 // --------------------------------------------------------------------------- 56 #if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__ICL) && !defined(__COMO__) && !defined(__BORLANDC__) 57 #define MSVC 58 # endif 59 60 61 namespace ESTLUtils { 62 63 // ------------------------------------------------------------------------- 64 // SequenceGenerator is a functor class template whose function objects 65 // generate arithmetic sequences of values. Rather than creating 66 // SequenceGenerator objects directly, clients are expected to use the 67 // convenience function makeSequence (see below). (This is analogous to 68 // how STL clients use make_pair() instead of creating pair<T1, T2> 69 // objects directly.) 70 // 71 // On the off chance you care, SequenceGenerator/makeSequence is a 72 // generalization of the "iota" algorithm that was in the original STL 73 // but that failed to make it into standard C++. 74 // ------------------------------------------------------------------------- 75 template<typename T> 76 class SequenceGenerator { 77 public: 78 SequenceGenerator(const T& initValue = T(), const T& increment = 1) nextVal(initValue)79 : nextVal(initValue), incr(increment) {} 80 operator()81 T operator()() 82 { 83 T valToReturn(nextVal); 84 nextVal += incr; 85 return valToReturn; 86 } 87 88 private: 89 T nextVal; 90 T incr; 91 }; 92 93 // ------------------------------------------------------------------------- 94 // The makeSequence functions generate objects that themselves generate 95 // sequences of values. By default, the values are of type int, the 96 // initial value is 0, and the increment between values is 1. All these 97 // defaults may be overridden. makeSequence is designed to be used with 98 // the generate_n algorithm to fill containers with values. Here are 99 // three examples of how it is expected to be used: 100 // 101 // list<int> L; 102 // generate_n(back_inserter(L), 5, // insert 0, 1, 2, 3, 4, and 5 103 // makeSequence()); // into L 104 // 105 // vector<int> v; 106 // generate_n(back_inserter(v), 3, // insert 50, 51, and 52 into v 107 // makeSequence(50)); 108 // 109 // deque<double> d; 110 // generate_n(back_inserter(d), 10, // insert 0.5, 1.5, ..., 9.5 111 // makeSequence(0.5, 1.0)); // into d 112 // 113 // Note that makeSequence() can't be used to generate values for maps or 114 // multimaps, because it generates sequences of individual values, not 115 // sequences of pairs. 116 // ------------------------------------------------------------------------- 117 inline makeSequence()118 SequenceGenerator<int> makeSequence() 119 { 120 return SequenceGenerator<int>(0, 1); 121 } 122 123 template<typename T> 124 inline 125 SequenceGenerator<T> makeSequence(const T& initValue = T(), 126 const T& increment = 1) 127 { 128 return SequenceGenerator<T>(initValue, increment); 129 } 130 131 132 // ------------------------------------------------------------------------- 133 // The following templates generate functions to print the contents of 134 // one or two containers: 135 // 136 // printContainer(container); 137 // printContainer(container, stream); 138 // printContainer(name, container); 139 // printContainer(name, container, stream); 140 // 141 // printContainers(container1, container2); 142 // printContainers(container1, container2, stream); 143 // printContainers(name1, container1, name2, container2); 144 // printContainers(name1, container1, name2, container2, stream); 145 // 146 // The next two are just macros (by LZ). They use the container's name 147 // as the name, and don't have "overloads" to specify a stream. They're 148 // for quick-and-dirty console debugging only: 149 // 150 // show(container1) 151 // show2(container1, container2) 152 // 153 // For containers of pointers other than char* and const char*, each of 154 // the above dereferences each pointer before printing. Containers of 155 // pairs (including maps and multimaps) print each pair like this: 156 // "(key,value)". 157 // 158 // For most platforms, the implementation is relatively straightforward: 159 // printContainers creates and invokes printValue objects, and printValue 160 // is partially specialized for pairs and pointer types and fully 161 // specialized for char* and const char* types. Alas, MSVC lacks support 162 // for partial specialization, so we have to play games from the world of 163 // template metaprogramming, and even those we have to modify to work 164 // around the lack of partial specialization. The MSVC implementation is 165 // based on a posting to comp.lang.c++.moderated by Aleksey Gurtovoy. 166 // ------------------------------------------------------------------------- 167 #ifndef MSVC 168 // Print a non-pointer value. (A specialization for when T=std::pair is 169 // below.) 170 template<typename T> 171 struct printValue { operatorprintValue172 void operator()(std::ostream& s, const T& val) const 173 { s << val; } 174 }; 175 176 // Print a pointed-to value. (Specializations for char* and const char* 177 // are below.) 178 template<typename T> 179 struct printValue<T*> { 180 void operator()(std::ostream& s, const T* pVal) const 181 { if (pVal) // Modified by LZ to handle null pointers 182 s << *pVal; 183 else 184 s << "(null)"; 185 } 186 }; 187 188 // print a const char* 189 template<> 190 struct printValue<const char*> { 191 void operator()(std::ostream& s, const char* pVal) const 192 { s << pVal; } 193 }; 194 195 // print a char* 196 template<> 197 struct printValue<char*> { 198 void operator()(std::ostream& s, char* pVal) const 199 { s << pVal; } 200 }; 201 202 // print a std::pair 203 template<typename K, typename V> 204 struct printValue<std::pair<K, V> > { 205 void operator()(std::ostream& s, const std::pair<K, V>& p) const 206 { 207 s << '('; 208 printValue<K>()(s, p.first); 209 s << ','; 210 printValue<V>()(s, p.second); 211 s << ')'; 212 } 213 }; 214 #else 215 // MSVC lacks partial template specialization, so the approach above 216 // won't work. Instead, we call TypeQuery::examine on an object of type 217 // T, and the result type of the function call is used as an additional 218 // argument to drive overloading resolution among several functions named 219 // printValue. 220 struct IsAPtr {}; // TypeQuery::examine returns this if T is 221 // a pointer. 222 struct IsAPair {}; // It returns this if T is a std::pair. 223 struct IsNothingSpecial {}; // Otherwise it returns this type. 224 225 struct TypeQuery { 226 static IsAPtr examine(const void*) { return IsAPtr(); } 227 228 template<typename K, typename V> 229 static IsAPair examine(const std::pair<K, V>&) { return IsAPair(); } 230 231 static IsNothingSpecial examine(...) { return IsNothingSpecial(); } 232 }; 233 234 // Print a non-pointer value. (A specialization for when T=std::pair is 235 // below.) 236 template<typename T> 237 inline 238 void printValue(std::ostream& s, const T& val, IsNothingSpecial) 239 { s << val; } 240 241 // Print a pointed-to value. (Specializations for char* and const char* 242 // are below.) 243 template<typename T> 244 inline 245 void printValue(std::ostream& s, const T& val, IsAPtr) 246 { 247 if (val) // Modified by LZ to handle null pointers 248 s << *val; 249 else 250 s << "(null)"; 251 } 252 253 // print a const char* 254 template<> 255 inline 256 void printValue<const char*>(std::ostream& s, const char * const & val, 257 IsAPtr) 258 { s << val; } 259 260 // print a char* 261 template<> 262 inline 263 void printValue<char*>(std::ostream& s, char * const & val, IsAPtr) 264 { s << val; } 265 266 // print a std::pair 267 template<typename K, typename V> 268 inline 269 void printValue(std::ostream& s, const std::pair<K, V>& p, 270 IsAPair) 271 { 272 s << '('; 273 printValue(s, p.first, TypeQuery::examine(p.first)); 274 s << ','; 275 printValue(s, p.second, TypeQuery::examine(p.second)); 276 s << ')'; 277 } 278 #endif 279 280 //////////////////////////////////////////////////////////////////////////// 281 // In case you're looking for it, here's the main printContainer function. 282 //////////////////////////////////////////////////////////////////////////// 283 template<typename Container> 284 inline 285 void printContainer(const Container& c, std::ostream& s = std::cout) 286 { 287 using namespace std; 288 289 // for non-MSVC, we could probably use for_each and bind1st to 290 // eliminate the explicit loop, but this works fine, and it's easier to 291 // understand for new STL users. 292 for (typename Container::const_iterator b = c.begin(); 293 b != c.end(); 294 ++b) { 295 #ifndef MSVC 296 printValue<typename Container::value_type>()(s, *b); 297 #else 298 printValue(s, *b, TypeQuery::examine(*b)); 299 #endif 300 s << ' '; 301 } 302 s << endl; 303 } 304 305 #ifndef MSVC 306 // This version of printContainer is for when the container is a string. 307 // It prints the contents of the string as you'd expect. 308 inline 309 void printContainer(const std::string& s, std::ostream& stream) 310 { 311 using namespace std; 312 313 stream << s << endl; 314 } 315 #else 316 // MSVC thinks the above function is a specialization of 317 // printContainer<Container> instead of a standalone function. To 318 // suppress that interpretation, we give this version of printContainer 319 // an extra, unused, parameter with a default value (so callers can 320 // ignore it). Thanks to Eric Merrill for the workaround. 321 inline 322 void printContainer(const std::string& s, std::ostream& stream = std::cout, 323 int /* dummyParameter */ = 0) 324 { 325 using namespace std; 326 327 stream << s << endl; 328 } 329 #endif 330 331 template<typename Container> 332 inline 333 void printContainer(const char *name, const Container& c, 334 std::ostream& s = std::cout) 335 { 336 using namespace std; 337 338 cout << name << ": "; 339 printContainer(c, s); 340 } 341 342 template<typename Container> 343 inline 344 void printContainer(const std::string& name, const Container& c, 345 std::ostream& s = std::cout) 346 { 347 printContainer(name.c_str(), c, s); 348 } 349 350 template<typename Container1, typename Container2> 351 inline 352 void printContainers(const Container1& c1, const Container2& c2, 353 std::ostream& s = std::cout) 354 { 355 printContainer(c1, s); 356 printContainer(c2, s); 357 } 358 359 template<typename Container1, typename Container2> 360 inline 361 void printContainers(const char *name1, const Container1& c1, 362 const char *name2, const Container2& c2, 363 std::ostream& s = std::cout) 364 { 365 printContainer(name1, c1, s); 366 printContainer(name2, c2, s); 367 } 368 369 template<typename Container1, typename Container2> 370 inline 371 void printContainers(const std::string& name1, const Container1& c1, 372 const std::string& name2, const Container2& c2, 373 std::ostream& s = std::cout) 374 { 375 printContainers(name1.c_str(), c1, name2.c_str(), c2, s); 376 } 377 378 379 380 // 381 // by LZ: Convenience macros to display containers using their names 382 // as the labels: 383 // 384 385 // 386 // show(Container) 387 // Display container's name and contents 388 389 #define show(container) ESTLUtils::printContainer(#container, container) 390 391 // 392 // show2(Container, Container) 393 // 394 395 #define show2(c1, c2) ESTLUtils::printContainers(#c1, c2, #c2, c2) 396 397 398 399 400 // ------------------------------------------------------------------------- 401 // A Timer object keeps track of CPU time used since the object was 402 // created or last reset. It implicitly converts to a double, so it can 403 // be used like this: 404 // 405 // Timer t; // begin timing some operation 406 // ... 407 // cout << t; // print out how much CPU time has elapsed 408 // // in seconds 409 // 410 // MSVC doesn't understand that the contents of <ctime> are supposed to 411 // be in std, so for MSVC, we make them global. Note that the macro 412 // CLOCKS_PER_SEC is global regardless. After all, it's a macro. 413 // 414 // This is a portable, but fairly crude way to time things. For a full 415 // discussion of its limitations, including an approach to overcoming 416 // them, consult Chapter 19 of "STL Tutorial and Reference Guide," 417 // (second edition), by David R. Musser, Gillmer J. Derge, and Atul 418 // Saini, Addison-Wesley, 2001. 419 // --------------------------------------------------------------------------- 420 #ifdef MSVC 421 #define STD_CLOCK_T clock_t 422 #define STD_CLOCK clock 423 #define STD_DIFFTIME difftime 424 #else 425 #define STD_CLOCK_T std::clock_t 426 #define STD_CLOCK std::clock 427 #define STD_DIFFTIME std::difftime 428 #endif 429 430 class Timer { 431 public: 432 Timer(): start(STD_CLOCK()) {} 433 434 operator double() const 435 { return (STD_CLOCK() - start) / static_cast<double>(CLOCKS_PER_SEC); } 436 437 void reset() { start = STD_CLOCK(); } 438 439 private: 440 STD_CLOCK_T start; 441 }; 442 443 } // namespace ESTLUtils 444 445 #endif 446