]> git.stg.codes - stg.git/blob - include/stdstring.h
Уменьшена зависимость между заголовочными файлами путем использования
[stg.git] / include / stdstring.h
1 // =============================================================================\r
2 //  FILE:  StdString.h\r
3 //  AUTHOR:     Joe O'Leary (with outside help noted in comments)\r
4 //  REMARKS:\r
5 //              This header file declares the CStdStr template.  This template derives\r
6 //              the Standard C++ Library basic_string<> template and add to it the\r
7 //              the following conveniences:\r
8 //                      - The full MFC CString set of functions (including implicit cast)\r
9 //                      - writing to/reading from COM IStream interfaces\r
10 //                      - Functional objects for use in STL algorithms\r
11 //\r
12 //              From this template, we intstantiate two classes:  CStdStringA and\r
13 //              CStdStringW.  The name "CStdString" is just a #define of one of these,\r
14 //              based upone the _UNICODE macro setting\r
15 //\r
16 //              This header also declares our own version of the MFC/ATL UNICODE-MBCS\r
17 //              conversion macros.  Our version looks exactly like the Microsoft's to\r
18 //              facilitate portability.\r
19 //\r
20 //      NOTE:\r
21 //              If you you use this in an MFC or ATL build, you should include either\r
22 //              afx.h or atlbase.h first, as appropriate.\r
23 //\r
24 //      PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:\r
25 //\r
26 //              Several people have helped me iron out problems and othewise improve\r
27 //              this class.  OK, this is a long list but in my own defense, this code\r
28 //              has undergone two major rewrites.  Many of the improvements became\r
29 //              necessary after I rewrote the code as a template.  Others helped me\r
30 //              improve the CString facade.\r
31 //\r
32 //              Anyway, these people are (in chronological order):\r
33 //\r
34 //                      - Pete the Plumber (???)\r
35 //                      - Julian Selman\r
36 //                      - Chris (of Melbsys)\r
37 //                      - Dave Plummer\r
38 //                      - John C Sipos\r
39 //                      - Chris Sells\r
40 //                      - Nigel Nunn\r
41 //                      - Fan Xia\r
42 //                      - Matthew Williams\r
43 //                      - Carl Engman\r
44 //                      - Mark Zeren\r
45 //                      - Craig Watson\r
46 //                      - Rich Zuris\r
47 //                      - Karim Ratib\r
48 //                      - Chris Conti\r
49 //                      - Baptiste Lepilleur\r
50 //                      - Greg Pickles\r
51 //                      - Jim Cline\r
52 //                      - Jeff Kohn\r
53 //                      - Todd Heckel\r
54 //                      - Ullrich Pollähne\r
55 //                      - Joe Vitaterna\r
56 //                      - Joe Woodbury\r
57 //                      - Aaron (no last name)\r
58 //                      - Joldakowski (???)\r
59 //                      - Scott Hathaway\r
60 //                      - Eric Nitzche\r
61 //                      - Pablo Presedo\r
62 //                      - Farrokh Nejadlotfi\r
63 //                      - Jason Mills\r
64 //                      - Igor Kholodov\r
65 //                      - Mike Crusader\r
66 //                      - John James\r
67 //                      - Wang Haifeng\r
68 //                      - Tim Dowty\r
69 //          - Arnt Witteveen\r
70 //          - Glen Maynard\r
71 //          - Paul DeMarco\r
72 //          - Bagira (full name?)\r
73 //          - Ronny Schulz\r
74 //          - Jakko Van Hunen\r
75 //                      - Charles G\r
76 //\r
77 //      REVISION HISTORY\r
78 //    2003-JUL-10 - Thanks to Charles G for making me realize my 'FmtArg' fixes\r
79 //                  had inadvertently broken the DLL-export code (which is\r
80 //                  normally commented out.  I had to move it up higher.  Also\r
81 //                                      this helped me catch a bug in ssicoll that would prevent\r
82 //                  compilation, otherwise.\r
83 //\r
84 //    2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste\r
85 //                  bug in one of the overloads of FmtArg.\r
86 //\r
87 //    2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes\r
88 //                  to help CStdString build on SGI and for pointing out an\r
89 //                  error in placement of my preprocessor macros for ssfmtmsg.\r
90 //\r
91 //    2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of\r
92 //                  SpanExcluding was not properly handling the case in which\r
93 //                  the string did NOT contain any of the given characters\r
94 //\r
95 //    2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me\r
96 //                  get this code working with Borland's free compiler as well\r
97 //                  as the Dev-C++ compiler (available free at SourceForge).\r
98 //\r
99 //    2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud\r
100 //                  but harmless warnings that were showing up on g++.  Glen\r
101 //                  also pointed out that some pre-declarations of FmtArg<>\r
102 //                  specializations were unnecessary (and no good on G++)\r
103 //\r
104 //    2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using\r
105 //                  static_cast<> in a place in which I should have been using\r
106 //                  reinterpret_cast<> (the ctor for unsigned char strings).\r
107 //                  That's what happens when I don't unit-test properly!\r
108 //                  Arnt also noticed that CString was silently correcting the\r
109 //                  'nCount' argument to Left() and Right() where CStdString was\r
110 //                  not (and crashing if it was bad).  That is also now fixed!\r
111 //\r
112 //        2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix\r
113 //                                      for) a conversion problem with non-ASCII MBCS characters.\r
114 //                                      CStdString is now used in my favorite commercial MP3 player!\r
115 //\r
116 //        2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the\r
117 //                                      assignment operators (for _bstr_t) that would cause compiler\r
118 //                                      errors when refcounting protection was turned off.\r
119 //\r
120 //        2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators\r
121 //                                      due to a conflict with the rel_ops operator!=.  Thanks to\r
122 //                                      John James for pointing this out.\r
123 //\r
124 //    2001-OCT-29 - Added a minor range checking fix for the Mid function to\r
125 //                                      make it as forgiving as CString's version is.  Thanks to\r
126 //                                      Igor Kholodov for noticing this.  \r
127 //                                - Added a specialization of std::swap for CStdString.  Thanks\r
128 //                                      to Mike Crusader for suggesting this!  It's commented out\r
129 //                                      because you're not supposed to inject your own code into the\r
130 //                                      'std' namespace.  But if you don't care about that, it's\r
131 //                                      there if you want it\r
132 //                                - Thanks to Jason Mills for catching a case where CString was\r
133 //                                      more forgiving in the Delete() function than I was.\r
134 //\r
135 //        2001-JUN-06 - I was violating the Standard name lookup rules stated\r
136 //                                      in [14.6.2(3)].  None of the compilers I've tried so\r
137 //                                      far apparently caught this but HP-UX aCC 3.30 did.  The\r
138 //                                      fix was to add 'this->' prefixes in many places.\r
139 //                                      Thanks to Farrokh Nejadlotfi for this!\r
140 //\r
141 //        2001-APR-27 - StreamLoad was calculating the number of BYTES in one\r
142 //                                      case, not characters.  Thanks to Pablo Presedo for this.\r
143 //\r
144 //    2001-FEB-23 - Replace() had a bug which caused infinite loops if the\r
145 //                                      source string was empty.  Fixed thanks to Eric Nitzsche.\r
146 //\r
147 //    2001-FEB-23 - Scott Hathaway was a huge help in providing me with the\r
148 //                                      ability to build CStdString on Sun Unix systems.  He\r
149 //                                      sent me detailed build reports about what works and what\r
150 //                                      does not.  If CStdString compiles on your Unix box, you\r
151 //                                      can thank Scott for it.\r
152 //\r
153 //        2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a\r
154 //                                      range check as CString's does.  Now fixed -- thanks!\r
155 //\r
156 //        2000-NOV-07 - Aaron pointed out that I was calling static member\r
157 //                                      functions of char_traits via a temporary.  This was not\r
158 //                                      technically wrong, but it was unnecessary and caused\r
159 //                                      problems for poor old buggy VC5.  Thanks Aaron!\r
160 //\r
161 //        2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match\r
162 //                                      what the CString::Find code really ends up doing.   I was\r
163 //                                      trying to match the docs.  Now I match the CString code\r
164 //                                - Joe also caught me truncating strings for GetBuffer() calls\r
165 //                                      when the supplied length was less than the current length.\r
166 //\r
167 //        2000-MAY-25 - Better support for STLPORT's Standard library distribution\r
168 //                                - Got rid of the NSP macro - it interfered with Koenig lookup\r
169 //                                - Thanks to Joe Woodbury for catching a TrimLeft() bug that\r
170 //                                      I introduced in January.  Empty strings were not getting\r
171 //                                      trimmed\r
172 //\r
173 //        2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind\r
174 //                                      is supposed to be a const function.\r
175 //\r
176 //        2000-MAR-07 - Thanks to Ullrich Pollähne for catching a range bug in one\r
177 //                                      of the overloads of assign.\r
178 //\r
179 //    2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!\r
180 //                                      Thanks to Todd Heckel for helping out with this.\r
181 //\r
182 //        2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the\r
183 //                                      Trim() function more efficient.\r
184 //                                - Thanks to Jeff Kohn for prompting me to find and fix a typo\r
185 //                                      in one of the addition operators that takes _bstr_t.\r
186 //                                - Got rid of the .CPP file -  you only need StdString.h now!\r
187 //\r
188 //        1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem\r
189 //                                      with my implementation of CStdString::FormatV in which\r
190 //                                      resulting string might not be properly NULL terminated.\r
191 //\r
192 //        1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment\r
193 //                                      bug that MS has not fixed.  CStdString did nothing to fix\r
194 //                                      it either but it does now!  The bug was: create a string\r
195 //                                      longer than 31 characters, get a pointer to it (via c_str())\r
196 //                                      and then assign that pointer to the original string object.\r
197 //                                      The resulting string would be empty.  Not with CStdString!\r
198 //\r
199 //        1999-OCT-06 - BufferSet was erasing the string even when it was merely\r
200 //                                      supposed to shrink it.  Fixed.  Thanks to Chris Conti.\r
201 //                                - Some of the Q172398 fixes were not checking for assignment-\r
202 //                                      to-self.  Fixed.  Thanks to Baptiste Lepilleur.\r
203 //\r
204 //        1999-AUG-20 - Improved Load() function to be more efficient by using \r
205 //                                      SizeOfResource().  Thanks to Rich Zuris for this.\r
206 //                                - Corrected resource ID constructor, again thanks to Rich.\r
207 //                                - Fixed a bug that occurred with UNICODE characters above\r
208 //                                      the first 255 ANSI ones.  Thanks to Craig Watson. \r
209 //                                - Added missing overloads of TrimLeft() and TrimRight().\r
210 //                                      Thanks to Karim Ratib for pointing them out\r
211 //\r
212 //        1999-JUL-21 - Made all calls to GetBuf() with no args check length first.\r
213 //\r
214 //        1999-JUL-10 - Improved MFC/ATL independence of conversion macros\r
215 //                                - Added SS_NO_REFCOUNT macro to allow you to disable any\r
216 //                                      reference-counting your basic_string<> impl. may do.\r
217 //                                - Improved ReleaseBuffer() to be as forgiving as CString.\r
218 //                                      Thanks for Fan Xia for helping me find this and to\r
219 //                                      Matthew Williams for pointing it out directly.\r
220 //\r
221 //        1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in\r
222 //                                      ToLower/ToUpper.  They should call GetBuf() instead of\r
223 //                                      data() in order to ensure the changed string buffer is not\r
224 //                                      reference-counted (in those implementations that refcount).\r
225 //\r
226 //        1999-JUL-01 - Added a true CString facade.  Now you can use CStdString as\r
227 //                                      a drop-in replacement for CString.  If you find this useful,\r
228 //                                      you can thank Chris Sells for finally convincing me to give\r
229 //                                      in and implement it.\r
230 //                                - Changed operators << and >> (for MFC CArchive) to serialize\r
231 //                                      EXACTLY as CString's do.  So now you can send a CString out\r
232 //                                      to a CArchive and later read it in as a CStdString.   I have\r
233 //                                      no idea why you would want to do this but you can. \r
234 //\r
235 //        1999-JUN-21 - Changed the CStdString class into the CStdStr template.\r
236 //                                - Fixed FormatV() to correctly decrement the loop counter.\r
237 //                                      This was harmless bug but a bug nevertheless.  Thanks to\r
238 //                                      Chris (of Melbsys) for pointing it out\r
239 //                                - Changed Format() to try a normal stack-based array before\r
240 //                                      using to _alloca().\r
241 //                                - Updated the text conversion macros to properly use code\r
242 //                                      pages and to fit in better in MFC/ATL builds.  In other\r
243 //                                      words, I copied Microsoft's conversion stuff again. \r
244 //                                - Added equivalents of CString::GetBuffer, GetBufferSetLength\r
245 //                                - new sscpy() replacement of CStdString::CopyString()\r
246 //                                - a Trim() function that combines TrimRight() and TrimLeft().\r
247 //\r
248 //        1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()\r
249 //                                      instead of _isspace()   Thanks to Dave Plummer for this.\r
250 //\r
251 //        1999-FEB-26 - Removed errant line (left over from testing) that #defined\r
252 //                                      _MFC_VER.  Thanks to John C Sipos for noticing this.\r
253 //\r
254 //        1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that\r
255 //                                      caused infinite recursion and stack overflow\r
256 //                                - Added member functions to simplify the process of\r
257 //                                      persisting CStdStrings to/from DCOM IStream interfaces \r
258 //                                - Added functional objects (e.g. StdStringLessNoCase) that\r
259 //                                      allow CStdStrings to be used as keys STL map objects with\r
260 //                                      case-insensitive comparison \r
261 //                                - Added array indexing operators (i.e. operator[]).  I\r
262 //                                      originally assumed that these were unnecessary and would be\r
263 //                                      inherited from basic_string.  However, without them, Visual\r
264 //                                      C++ complains about ambiguous overloads when you try to use\r
265 //                                      them.  Thanks to Julian Selman to pointing this out. \r
266 //\r
267 //        1998-FEB-?? - Added overloads of assign() function to completely account\r
268 //                                      for Q172398 bug.  Thanks to "Pete the Plumber" for this\r
269 //\r
270 //        1998-FEB-?? - Initial submission\r
271 //\r
272 // COPYRIGHT:\r
273 //              2002 Joseph M. O'Leary.  This code is 100% free.  Use it anywhere you\r
274 //      want.  Rewrite it, restructure it, whatever.  If you can write software\r
275 //      that makes money off of it, good for you.  I kinda like capitalism. \r
276 //      Please don't blame me if it causes your $30 billion dollar satellite\r
277 //      explode in orbit.  If you redistribute it in any form, I'd appreciate it\r
278 //      if you would leave this notice here.\r
279 //\r
280 //              If you find any bugs, please let me know:\r
281 //\r
282 //                              jmoleary@earthlink.net\r
283 //                              http://www.joeo.net\r
284 //\r
285 //      The latest version of this code should always be available at the\r
286 //      following link:\r
287 //\r
288 //              http://www.joeo.net/code/StdString.zip\r
289 // =============================================================================\r
290 \r
291 // Avoid multiple inclusion the VC++ way,\r
292 // Turn off browser references\r
293 // Turn off unavoidable compiler warnings\r
294 \r
295 #if defined(_MSC_VER) && (_MSC_VER > 1100)\r
296         #pragma once\r
297         #pragma component(browser, off, references, "CStdString")\r
298         #pragma warning (disable : 4290) // C++ Exception Specification ignored\r
299         #pragma warning (disable : 4127) // Conditional expression is constant\r
300         #pragma warning (disable : 4097) // typedef name used as synonym for class name\r
301 #endif\r
302 \r
303 // Borland warnings to turn off\r
304 #ifdef __BORLANDC__\r
305     #pragma option push -w-inl\r
306 //      #pragma warn -inl   // Turn off inline function warnings\r
307 #endif\r
308 \r
309 #ifndef STDSTRING_H\r
310 #define STDSTRING_H\r
311 \r
312 // MACRO: SS_UNSIGNED\r
313 // ------------------\r
314 //      This macro causes the addition of a constructor and assignment operator\r
315 //      which take unsigned characters.  CString has such functions and in order\r
316 //      to provide maximum CString-compatability, this code needs them as well.\r
317 //      In practice you will likely never need these functions...\r
318 \r
319 //#define SS_UNSIGNED\r
320 \r
321 #ifdef SS_ALLOW_UNSIGNED_CHARS\r
322         #define SS_UNSIGNED\r
323 #endif\r
324 \r
325 // MACRO: SS_SAFE_FORMAT\r
326 // ---------------------\r
327 //      This macro provides limited compatability with a questionable CString\r
328 //      "feature".  You can define it in order to avoid a common problem that\r
329 //      people encounter when switching from CString to CStdString.\r
330 //\r
331 //      To illustrate the problem -- With CString, you can do this:\r
332 //\r
333 //          CString sName("Joe");\r
334 //          CString sTmp;\r
335 //          sTmp.Format("My name is %s", sName);                    // WORKS!\r
336 //\r
337 //      However if you were to try this with CStdString, your program would\r
338 //      crash.\r
339 //\r
340 //          CStdString sName("Joe");\r
341 //          CStdString sTmp;\r
342 //          sTmp.Format("My name is %s", sName);                    // CRASHES!\r
343 //\r
344 //      You must explicitly call c_str() or cast the object to the proper type\r
345 //\r
346 //          sTmp.Format("My name is %s", sName.c_str());            // WORKS!\r
347 //          sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS!\r
348 //          sTmp.Format("My name is %s", (PCSTR)sName);// WORKS!\r
349 //\r
350 //      This is because it is illegal to pass anything but a POD type as a\r
351 //      variadic argument to a variadic function (i.e. as one of the "..."\r
352 //      arguments).  The type const char* is a POD type.  The type CStdString\r
353 //      is not.  Of course, neither is the type CString, but CString lets you do\r
354 //      it anyway due to the way they laid out the class in binary.  I have no\r
355 //      control over this in CStdString since I derive from whatever\r
356 //      implementation of basic_string is available.\r
357 //\r
358 //      However if you have legacy code (which does this) that you want to take\r
359 //      out of the MFC world and you don't want to rewrite all your calls to\r
360 //      Format(), then you can define this flag and it will no longer crash.\r
361 //\r
362 //      Note however that this ONLY works for Format(), not sprintf, fprintf, \r
363 //      etc.  If you pass a CStdString object to one of those functions, your\r
364 //      program will crash.  Not much I can do to get around this, short of\r
365 //      writing substitutes for those functions as well.\r
366 \r
367 #define SS_SAFE_FORMAT  // use new template style Format() function\r
368 \r
369 \r
370 // MACRO: SS_NO_IMPLICIT_CAST\r
371 // --------------------------\r
372 //      Some people don't like the implicit cast to const char* (or rather to\r
373 //      const CT*) that CStdString (and MFC's CString) provide.  That was the\r
374 //      whole reason I created this class in the first place, but hey, whatever\r
375 //      bakes your cake.  Just #define this macro to get rid of the the implicit\r
376 //      cast.\r
377 \r
378 //#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*()\r
379 \r
380 \r
381 // MACRO: SS_NO_REFCOUNT\r
382 // ---------------------\r
383 //              turns off reference counting at the assignment level.  Only needed\r
384 //              for the version of basic_string<> that comes with Visual C++ versions\r
385 //              6.0 or earlier, and only then in some heavily multithreaded scenarios.\r
386 //              Uncomment it if you feel you need it.\r
387 \r
388 //#define SS_NO_REFCOUNT\r
389 \r
390 // MACRO: SS_WIN32\r
391 // ---------------\r
392 //      When this flag is set, we are building code for the Win32 platform and\r
393 //      may use Win32 specific functions (such as LoadString).  This gives us\r
394 //      a couple of nice extras for the code.\r
395 //\r
396 //      Obviously, Microsoft's is not the only compiler available for Win32 out\r
397 //      there.  So I can't just check to see if _MSC_VER is defined to detect\r
398 //      if I'm building on Win32.  So for now, if you use MS Visual C++ or\r
399 //      Borland's compiler, I turn this on.  Otherwise you may turn it on\r
400 //      yourself, if you prefer\r
401 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32)\r
402     #define SS_WIN32\r
403 #endif\r
404 \r
405 // MACRO: SS_ANSI\r
406 // --------------\r
407 //      When this macro is defined, the code attempts only to use ANSI/ISO\r
408 //      standard library functions to do it's work.  It will NOT attempt to use\r
409 //      any Win32 of Visual C++ specific functions -- even if they are\r
410 //      available.  You may define this flag yourself to prevent any Win32\r
411 //      of VC++ specific functions from being called.\r
412 \r
413 // If we're not on Win32, we MUST use an ANSI build\r
414 #ifndef SS_WIN32\r
415     #if !defined(SS_NO_ANSI)\r
416         #define SS_ANSI\r
417     #endif\r
418 #endif\r
419 \r
420 // MACRO: SS_ALLOCA\r
421 // ----------------\r
422 //      Some implementations of the Standard C Library have a non-standard\r
423 //      function known as alloca().  This functions allows one to allocate a\r
424 //      variable amount of memory on the stack.  It comes in very useful for\r
425 //      the ASCII/MBCS conversion macros.\r
426 //\r
427 //      Here we attempt to determine automatically if alloca() is available on\r
428 //      this platform.  If so we define SS_ALLOCA to be the name of the alloca\r
429 //      function.  If SS_ALLOCA is undefined later on, then the conversion\r
430 //      macros will not be compiled.\r
431 //\r
432 //      You may prevent SS_ALLOCA\r
433 \r
434 \r
435 \r
436 // Avoid legacy code screw up: if _UNICODE is defined, UNICODE must be as well\r
437 \r
438 #if defined (_UNICODE) && !defined (UNICODE)\r
439         #define UNICODE\r
440 #endif\r
441 #if defined (UNICODE) && !defined (_UNICODE)\r
442         #define _UNICODE\r
443 #endif\r
444 \r
445 // -----------------------------------------------------------------------------\r
446 // MIN and MAX.  The Standard C++ template versions go by so many names (at\r
447 // at least in the MS implementation) that you never know what's available\r
448 // -----------------------------------------------------------------------------\r
449 template<class Type>\r
450 inline const Type& SSMIN(const Type& arg1, const Type& arg2)\r
451 {\r
452         return arg2 < arg1 ? arg2 : arg1;\r
453 }\r
454 template<class Type>\r
455 inline const Type& SSMAX(const Type& arg1, const Type& arg2)\r
456 {\r
457         return arg2 > arg1 ? arg2 : arg1;\r
458 }\r
459 \r
460 // If they have not #included W32Base.h (part of my W32 utility library) then\r
461 // we need to define some stuff.  Otherwise, this is all defined there.\r
462 \r
463 #if !defined(W32BASE_H)\r
464 \r
465         // If they want us to use only standard C++ stuff (no Win32 stuff)\r
466 \r
467         #ifdef SS_ANSI\r
468 \r
469                 // On Win32 we have TCHAR.H so just include it.  This is NOT violating\r
470         // the spirit of SS_ANSI as we are not calling any Win32 functions here.\r
471         \r
472                 #ifdef SS_WIN32\r
473 \r
474                         #include <TCHAR.H>\r
475                         #include <WTYPES.H>\r
476                         #ifndef STRICT\r
477                                 #define STRICT\r
478                         #endif\r
479 \r
480         // ... but on non-Win32 platforms, we must #define the types we need.\r
481 \r
482                 #else\r
483 \r
484                         typedef const char*             PCSTR;\r
485                         typedef char*                   PSTR;\r
486                         typedef const wchar_t*  PCWSTR;\r
487                         typedef wchar_t*                PWSTR;\r
488                         #ifdef UNICODE\r
489                                 typedef wchar_t         TCHAR;\r
490                         #else\r
491                                 typedef char            TCHAR;\r
492                         #endif\r
493                         typedef wchar_t                 OLECHAR;\r
494 \r
495                 #endif  // #ifndef _WIN32\r
496 \r
497 \r
498                 // Make sure ASSERT and verify are defined using only ANSI stuff\r
499 \r
500                 #ifndef ASSERT\r
501                         #include <assert.h>\r
502                         #define ASSERT(f) assert((f))\r
503                 #endif\r
504                 #ifndef VERIFY\r
505                         #ifdef _DEBUG\r
506                                 #define VERIFY(x) ASSERT((x))\r
507                         #else\r
508                                 #define VERIFY(x) x\r
509                         #endif\r
510                 #endif\r
511 \r
512         #else // ...else SS_ANSI is NOT defined\r
513 \r
514                 #include <TCHAR.H>\r
515                 #include <WTYPES.H>\r
516                 #ifndef STRICT\r
517                         #define STRICT\r
518                 #endif\r
519 \r
520                 // Make sure ASSERT and verify are defined\r
521 \r
522                 #ifndef ASSERT\r
523                         #include <crtdbg.h>\r
524                         #define ASSERT(f) _ASSERTE((f))\r
525                 #endif\r
526                 #ifndef VERIFY\r
527                         #ifdef _DEBUG\r
528                                 #define VERIFY(x) ASSERT((x))\r
529                         #else\r
530                                 #define VERIFY(x) x\r
531                         #endif\r
532                 #endif\r
533 \r
534         #endif // #ifdef SS_ANSI\r
535 \r
536         #ifndef UNUSED\r
537                 #define UNUSED(x) x\r
538         #endif\r
539 \r
540 #endif // #ifndef W32BASE_H\r
541 \r
542 // Standard headers needed\r
543 \r
544 #include <string>                       // basic_string\r
545 #include <algorithm>            // for_each, etc.\r
546 #include <functional>           // for StdStringLessNoCase, et al\r
547 #include <locale>               // for various facets\r
548 \r
549 // If this is a recent enough version of VC include comdef.h, so we can write\r
550 // member functions to deal with COM types & compiler support classes e.g. _bstr_t\r
551 \r
552 #if defined (_MSC_VER) && (_MSC_VER >= 1100)\r
553         #include <comdef.h>\r
554         #define SS_INC_COMDEF           // signal that we #included MS comdef.h file\r
555         #define STDSTRING_INC_COMDEF\r
556         #define SS_NOTHROW __declspec(nothrow)\r
557 #else\r
558         #define SS_NOTHROW\r
559 #endif\r
560 \r
561 #ifndef TRACE\r
562         #define TRACE_DEFINED_HERE\r
563         #define TRACE\r
564 #endif\r
565 \r
566 // Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR.  I hate to use the\r
567 // versions with the "L" in front of them because that's a leftover from Win 16\r
568 // days, even though it evaluates to the same thing.  Therefore, Define a PCSTR\r
569 // as an LPCTSTR.\r
570 \r
571 #if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)\r
572         typedef const TCHAR*                    PCTSTR;\r
573         #define PCTSTR_DEFINED\r
574 #endif\r
575 \r
576 #if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)\r
577         typedef const OLECHAR*                  PCOLESTR;\r
578         #define PCOLESTR_DEFINED\r
579 #endif\r
580 \r
581 #if !defined(POLESTR) && !defined(POLESTR_DEFINED)\r
582         typedef OLECHAR*                                POLESTR;\r
583         #define POLESTR_DEFINED\r
584 #endif\r
585 \r
586 #if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)\r
587         typedef const unsigned char*    PCUSTR;\r
588         typedef unsigned char*                  PUSTR;\r
589         #define PCUSTR_DEFINED\r
590 #endif\r
591 \r
592 \r
593 // SGI compiler 7.3 doesnt know these  types - oh and btw, remember to use\r
594 // -LANG:std in the CXX Flags\r
595 #if defined(__sgi)\r
596     typedef unsigned long           DWORD;\r
597     typedef void *                  LPCVOID;\r
598 #endif\r
599 \r
600 \r
601 // SS_USE_FACET macro and why we need it:\r
602 //\r
603 // Since I'm a good little Standard C++ programmer, I use locales.  Thus, I\r
604 // need to make use of the use_facet<> template function here.   Unfortunately,\r
605 // this need is complicated by the fact the MS' implementation of the Standard\r
606 // C++ Library has a non-standard version of use_facet that takes more\r
607 // arguments than the standard dictates.  Since I'm trying to write CStdString\r
608 // to work with any version of the Standard library, this presents a problem.\r
609 //\r
610 // The upshot of this is that I can't do 'use_facet' directly.  The MS' docs\r
611 // tell me that I have to use a macro, _USE() instead.  Since _USE obviously\r
612 // won't be available in other implementations, this means that I have to write\r
613 // my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the\r
614 // standard, use_facet.\r
615 //\r
616 // If you are having trouble with the SS_USE_FACET macro, in your implementation\r
617 // of the Standard C++ Library, you can define your own version of SS_USE_FACET.\r
618 #ifndef schMSG\r
619         #define schSTR(x)          #x\r
620         #define schSTR2(x)      schSTR(x)\r
621         #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)\r
622 #endif\r
623 \r
624 #ifndef SS_USE_FACET\r
625         // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for\r
626         // all MSVC builds, erroneously in my opinion.  It causes problems for\r
627         // my SS_ANSI builds.  In my code, I always comment out that line.  You'll\r
628         // find it in   \stlport\config\stl_msvc.h\r
629         #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )\r
630                 #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)\r
631                         #ifdef SS_ANSI\r
632                                 #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)\r
633                         #endif\r
634                 #endif\r
635                 #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)\r
636         #elif defined(_MSC_VER )\r
637         #define SS_USE_FACET(loc, fac) std::_USE(loc, fac)\r
638 \r
639         // ...and\r
640         #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)\r
641         #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)\r
642         #else\r
643                 #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)\r
644         #endif\r
645 #endif\r
646 \r
647 // =============================================================================\r
648 // UNICODE/MBCS conversion macros.  Made to work just like the MFC/ATL ones.\r
649 // =============================================================================\r
650 \r
651 #include <wchar.h>      // Added to Std Library with Amendment #1.\r
652 \r
653 // First define the conversion helper functions.  We define these regardless of\r
654 // any preprocessor macro settings since their names won't collide. \r
655 \r
656 // Not sure if we need all these headers.   I believe ANSI says we do.\r
657 \r
658 #include <stdio.h>\r
659 #include <stdarg.h>\r
660 #include <wctype.h>\r
661 #include <ctype.h>\r
662 #include <stdlib.h>\r
663 #ifndef va_start\r
664         #include <varargs.h>\r
665 #endif\r
666 \r
667 // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte\r
668 //                              and MultiByteToWideChar but uses locales in SS_ANSI\r
669 //                              builds\r
670 //typedef int mbstate_t;\r
671 #if defined (SS_ANSI) || !defined (SS_WIN32)\r
672 \r
673     typedef std::codecvt<wchar_t, char, std::mbstate_t> SSCodeCvt;\r
674 \r
675 \r
676     inline PWSTR StdCodeCvt(PWSTR pW, PCSTR pA, int nChars,\r
677         const std::locale& loc=std::locale())\r
678     {\r
679         ASSERT(0 != pA);\r
680         ASSERT(0 != pW);\r
681         pW[0] = '\0';\r
682         PCSTR pBadA                             = 0;\r
683         PWSTR pBadW                             = 0;\r
684         SSCodeCvt::result res   = SSCodeCvt::ok;\r
685         const SSCodeCvt& conv   = SS_USE_FACET(loc, SSCodeCvt);\r
686         SSCodeCvt::state_type st= { 0 };\r
687         res                                             = conv.in(st,\r
688                                                                           pA, pA + nChars, pBadA,\r
689                                                                           pW, pW + nChars, pBadW);\r
690         ASSERT(SSCodeCvt::ok == res);\r
691         return pW;\r
692     }\r
693     inline PWSTR StdCodeCvt(PWSTR pW, PCUSTR pA, int nChars,\r
694         const std::locale& loc=std::locale())\r
695     {\r
696         return StdCodeCvt(pW, (PCSTR)pA, nChars, loc);\r
697     }\r
698 \r
699     inline PSTR StdCodeCvt(PSTR pA, PCWSTR pW, int nChars,\r
700         const std::locale& loc=std::locale())\r
701     {\r
702         ASSERT(0 != pA);\r
703         ASSERT(0 != pW);\r
704         pA[0] = '\0';\r
705         PSTR pBadA                              = 0;\r
706         PCWSTR pBadW                    = 0;\r
707         SSCodeCvt::result res   = SSCodeCvt::ok;\r
708         const SSCodeCvt& conv   = SS_USE_FACET(loc, SSCodeCvt);\r
709         SSCodeCvt::state_type st= { 0 };\r
710         res                                             = conv.out(st,\r
711                                                                            pW, pW + nChars, pBadW,\r
712                                                                            pA, pA + nChars, pBadA);\r
713         ASSERT(SSCodeCvt::ok == res);\r
714         return pA;\r
715     }\r
716     inline PUSTR StdCodeCvt(PUSTR pA, PCWSTR pW, int nChars,\r
717         const std::locale& loc=std::locale())\r
718     {\r
719         return (PUSTR)StdCodeCvt((PSTR)pA, pW, nChars, loc);\r
720     }\r
721 \r
722 #else   // ...or are we doing things assuming win32 and Visual C++?\r
723 \r
724         #include <malloc.h>     // needed for _alloca\r
725 \r
726         inline PWSTR StdCodeCvt(PWSTR pW, PCSTR pA, int nChars, UINT acp=CP_ACP)\r
727         {\r
728                 ASSERT(0 != pA);\r
729                 ASSERT(0 != pW);\r
730                 pW[0] = '\0';\r
731                 MultiByteToWideChar(acp, 0, pA, -1, pW, nChars);\r
732                 return pW;\r
733         }\r
734         inline PWSTR StdCodeCvt(PWSTR pW, PCUSTR pA, int nChars, UINT acp=CP_ACP)\r
735         {\r
736                 return StdCodeCvt(pW, (PCSTR)pA, nChars, acp);\r
737         }\r
738 \r
739         inline PSTR StdCodeCvt(PSTR pA, PCWSTR pW, int nChars, UINT acp=CP_ACP)\r
740         {\r
741                 ASSERT(0 != pA);\r
742                 ASSERT(0 != pW);\r
743                 pA[0] = '\0';\r
744                 WideCharToMultiByte(acp, 0, pW, -1, pA, nChars, 0, 0);\r
745                 return pA;\r
746         }\r
747         inline PUSTR StdCodeCvt(PUSTR pA, PCWSTR pW, int nChars, UINT acp=CP_ACP)\r
748         {\r
749                 return (PUSTR)StdCodeCvt((PSTR)pA, pW, nChars, acp);\r
750         }\r
751 \r
752 #endif\r
753 // Unicode/MBCS conversion macros are only available on implementations of\r
754 // the "C" library that have the non-standard _alloca function.  As far as I\r
755 // know that's only Microsoft's though I've hear that the function exits\r
756 // elsewhere.  \r
757     \r
758 #if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION\r
759 \r
760     #include <malloc.h> // needed for _alloca\r
761 \r
762 \r
763     // Define our conversion macros to look exactly like Microsoft's to\r
764     // facilitate using this stuff both with and without MFC/ATL\r
765 \r
766     #ifdef _CONVERSION_USES_THREAD_LOCALE\r
767             #ifndef _DEBUG\r
768                     #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \\r
769                             _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa\r
770             #else\r
771                     #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\\r
772                              _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa\r
773             #endif\r
774     #else\r
775             #ifndef _DEBUG\r
776                     #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\\r
777                              PCWSTR _pw; _pw; PCSTR _pa; _pa\r
778             #else\r
779                     #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \\r
780                             _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa\r
781             #endif\r
782     #endif\r
783 \r
784     #ifdef _CONVERSION_USES_THREAD_LOCALE\r
785             #define SSA2W(pa) (\\r
786                     ((_pa = pa) == 0) ? 0 : (\\r
787                             _cvt = (sslen(_pa)+1),\\r
788                             StdCodeCvt((PWSTR) _alloca(_cvt*2), _pa, _cvt, _acp)))\r
789             #define SSW2A(pw) (\\r
790                     ((_pw = pw) == 0) ? 0 : (\\r
791                             _cvt = (sslen(_pw)+1)*2,\\r
792                             StdW2AHelper((LPSTR) _alloca(_cvt), _pw, _cvt, _acp)))\r
793     #else\r
794             #define SSA2W(pa) (\\r
795                     ((_pa = pa) == 0) ? 0 : (\\r
796                             _cvt = (sslen(_pa)+1),\\r
797                             StdCodeCvt((PWSTR) _alloca(_cvt*2), _pa, _cvt)))\r
798             #define SSW2A(pw) (\\r
799                     ((_pw = pw) == 0) ? 0 : (\\r
800                             _cvt = (sslen(_pw)+1)*2,\\r
801                             StdCodeCvt((LPSTR) _alloca(_cvt), _pw, _cvt)))\r
802     #endif\r
803 \r
804     #define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))\r
805     #define SSW2CA(pw) ((PCSTR)SSW2A((pw)))\r
806 \r
807     #ifdef UNICODE\r
808             #define SST2A       SSW2A\r
809             #define SSA2T       SSA2W\r
810             #define SST2CA      SSW2CA\r
811             #define SSA2CT      SSA2CW\r
812             inline PWSTR        SST2W(PTSTR p)                  { return p; }\r
813             inline PTSTR        SSW2T(PWSTR p)                  { return p; }\r
814             inline PCWSTR       SST2CW(PCTSTR p)                { return p; }\r
815             inline PCTSTR       SSW2CT(PCWSTR p)                { return p; }\r
816     #else\r
817             #define SST2W       SSA2W\r
818             #define SSW2T       SSW2A\r
819             #define SST2CW      SSA2CW\r
820             #define SSW2CT      SSW2CA\r
821             inline PSTR         SST2A(PTSTR p)                  { return p; }\r
822             inline PTSTR        SSA2T(PSTR p)                   { return p; }\r
823             inline PCSTR        SST2CA(PCTSTR p)                { return p; }\r
824             inline PCTSTR       SSA2CT(PCSTR p)                 { return p; }\r
825     #endif // #ifdef UNICODE\r
826 \r
827     #if defined(UNICODE)\r
828     // in these cases the default (TCHAR) is the same as OLECHAR\r
829             inline PCOLESTR     SST2COLE(PCTSTR p)              { return p; }\r
830             inline PCTSTR       SSOLE2CT(PCOLESTR p)    { return p; }\r
831             inline POLESTR      SST2OLE(PTSTR p)                { return p; }\r
832             inline PTSTR        SSOLE2T(POLESTR p)              { return p; }\r
833     #elif defined(OLE2ANSI)\r
834     // in these cases the default (TCHAR) is the same as OLECHAR\r
835             inline PCOLESTR     SST2COLE(PCTSTR p)              { return p; }\r
836             inline PCTSTR       SSOLE2CT(PCOLESTR p)    { return p; }\r
837             inline POLESTR      SST2OLE(PTSTR p)                { return p; }\r
838             inline PTSTR        SSOLE2T(POLESTR p)              { return p; }\r
839     #else\r
840             //CharNextW doesn't work on Win95 so we use this\r
841             #define SST2COLE(pa)        SSA2CW((pa))\r
842             #define SST2OLE(pa)         SSA2W((pa))\r
843             #define SSOLE2CT(po)        SSW2CA((po))\r
844             #define SSOLE2T(po)         SSW2A((po))\r
845     #endif\r
846 \r
847     #ifdef OLE2ANSI\r
848             #define SSW2OLE             SSW2A\r
849             #define SSOLE2W             SSA2W\r
850             #define SSW2COLE    SSW2CA\r
851             #define SSOLE2CW    SSA2CW\r
852             inline POLESTR              SSA2OLE(PSTR p)         { return p; }\r
853             inline PSTR                 SSOLE2A(POLESTR p)      { return p; }\r
854             inline PCOLESTR             SSA2COLE(PCSTR p)       { return p; }\r
855             inline PCSTR                SSOLE2CA(PCOLESTR p){ return p; }\r
856     #else\r
857             #define SSA2OLE             SSA2W\r
858             #define SSOLE2A             SSW2A\r
859             #define SSA2COLE    SSA2CW\r
860             #define SSOLE2CA    SSW2CA\r
861             inline POLESTR              SSW2OLE(PWSTR p)        { return p; }\r
862             inline PWSTR                SSOLE2W(POLESTR p)      { return p; }\r
863             inline PCOLESTR             SSW2COLE(PCWSTR p)      { return p; }\r
864             inline PCWSTR               SSOLE2CW(PCOLESTR p){ return p; }\r
865     #endif\r
866 \r
867     // Above we've defined macros that look like MS' but all have\r
868     // an 'SS' prefix.  Now we need the real macros.  We'll either\r
869     // get them from the macros above or from MFC/ATL. \r
870 \r
871         #if defined (USES_CONVERSION)\r
872 \r
873                 #define _NO_STDCONVERSION       // just to be consistent\r
874 \r
875         #else\r
876 \r
877                 #ifdef _MFC_VER\r
878 \r
879                         #include <afxconv.h>\r
880                         #define _NO_STDCONVERSION // just to be consistent\r
881 \r
882                 #else\r
883 \r
884                         #define USES_CONVERSION SSCVT\r
885                         #define A2CW                    SSA2CW\r
886                         #define W2CA                    SSW2CA\r
887                         #define T2A                             SST2A\r
888                         #define A2T                             SSA2T\r
889                         #define T2W                             SST2W\r
890                         #define W2T                             SSW2T\r
891                         #define T2CA                    SST2CA\r
892                         #define A2CT                    SSA2CT\r
893                         #define T2CW                    SST2CW\r
894                         #define W2CT                    SSW2CT\r
895                         #define ocslen                  sslen\r
896                         #define ocscpy                  sscpy\r
897                         #define T2COLE                  SST2COLE\r
898                         #define OLE2CT                  SSOLE2CT\r
899                         #define T2OLE                   SST2COLE\r
900                         #define OLE2T                   SSOLE2CT\r
901                         #define A2OLE                   SSA2OLE\r
902                         #define OLE2A                   SSOLE2A\r
903                         #define W2OLE                   SSW2OLE\r
904                         #define OLE2W                   SSOLE2W\r
905                         #define A2COLE                  SSA2COLE\r
906                         #define OLE2CA                  SSOLE2CA\r
907                         #define W2COLE                  SSW2COLE\r
908                         #define OLE2CW                  SSOLE2CW\r
909         \r
910                 #endif // #ifdef _MFC_VER\r
911         #endif // #ifndef USES_CONVERSION\r
912 #endif // #ifndef SS_NO_CONVERSION\r
913 \r
914 // Define ostring - generic name for std::basic_string<OLECHAR>\r
915 \r
916 #if !defined(ostring) && !defined(OSTRING_DEFINED)\r
917         typedef std::basic_string<OLECHAR> ostring;\r
918         #define OSTRING_DEFINED\r
919 #endif\r
920 \r
921 // StdCodeCvt when there's no conversion to be done\r
922 inline PSTR StdCodeCvt(PSTR pDst, PCSTR pSrc, int nChars)\r
923 {\r
924         if ( nChars > 0 )\r
925         {\r
926                 pDst[0]                         = '\0';\r
927                 std::basic_string<char>::traits_type::copy(pDst, pSrc, nChars);\r
928 //              std::char_traits<char>::copy(pDst, pSrc, nChars);\r
929                 if ( nChars > 0 )\r
930                         pDst[nChars]    = '\0';\r
931         }\r
932 \r
933         return pDst;\r
934 }\r
935 inline PSTR StdCodeCvt(PSTR pDst, PCUSTR pSrc, int nChars)\r
936 {\r
937         return StdCodeCvt(pDst, (PCSTR)pSrc, nChars);\r
938 }\r
939 inline PUSTR StdCodeCvt(PUSTR pDst, PCSTR pSrc, int nChars)\r
940 {\r
941         return (PUSTR)StdCodeCvt((PSTR)pDst, pSrc, nChars);\r
942 }\r
943 \r
944 inline PWSTR StdCodeCvt(PWSTR pDst, PCWSTR pSrc, int nChars)\r
945 {\r
946         if ( nChars > 0 )\r
947         {\r
948                 pDst[0]                         = '\0';\r
949                 std::basic_string<wchar_t>::traits_type::copy(pDst, pSrc, nChars);\r
950 //              std::char_traits<wchar_t>::copy(pDst, pSrc, nChars);\r
951                 if ( nChars > 0 )\r
952                         pDst[nChars]    = '\0';\r
953         }\r
954 \r
955         return pDst;\r
956 }\r
957 \r
958 \r
959 // Define tstring -- generic name for std::basic_string<TCHAR>\r
960 \r
961 #if !defined(tstring) && !defined(TSTRING_DEFINED)\r
962         typedef std::basic_string<TCHAR> tstring;\r
963         #define TSTRING_DEFINED\r
964 #endif\r
965 \r
966 // a very shorthand way of applying the fix for KB problem Q172398\r
967 // (basic_string assignment bug)\r
968 \r
969 #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )\r
970         #define Q172398(x) (x).erase()\r
971 #else\r
972         #define Q172398(x)\r
973 #endif\r
974 \r
975 // =============================================================================\r
976 // INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES\r
977 //\r
978 // Usually for generic text mapping, we rely on preprocessor macro definitions\r
979 // to map to string functions.  However the CStdStr<> template cannot use\r
980 // macro-based generic text mappings because its character types do not get\r
981 // resolved until template processing which comes AFTER macro processing.  In\r
982 // other words, UNICODE is of little help to us in the CStdStr template\r
983 //\r
984 // Therefore, to keep the CStdStr declaration simple, we have these inline\r
985 // functions.  The template calls them often.  Since they are inline (and NOT\r
986 // exported when this is built as a DLL), they will probably be resolved away\r
987 // to nothing. \r
988 //\r
989 // Without these functions, the CStdStr<> template would probably have to broken\r
990 // out into two, almost identical classes.  Either that or it would be a huge,\r
991 // convoluted mess, with tons of "if" statements all over the place checking the\r
992 // size of template parameter CT.\r
993 // \r
994 // In several cases, you will see two versions of each function.  One version is\r
995 // the more portable, standard way of doing things, while the other is the\r
996 // non-standard, but often significantly faster Visual C++ way.\r
997 // =============================================================================\r
998 \r
999 // If they defined SS_NO_REFCOUNT, then we must convert all assignments\r
1000 \r
1001 #ifdef SS_NO_REFCOUNT\r
1002         #define SSREF(x) (x).c_str()\r
1003 #else\r
1004         #define SSREF(x) (x)\r
1005 #endif\r
1006 \r
1007 // -----------------------------------------------------------------------------\r
1008 // sslen: strlen/wcslen wrappers\r
1009 // -----------------------------------------------------------------------------\r
1010 template<typename CT> inline int sslen(const CT* pT)\r
1011 {\r
1012         return 0 == pT ? 0 : std::basic_string<CT>::traits_type::length(pT);\r
1013 //      return 0 == pT ? 0 : std::char_traits<CT>::length(pT);\r
1014 }\r
1015 inline SS_NOTHROW int sslen(const std::string& s)\r
1016 {\r
1017         return s.length();\r
1018 }\r
1019 inline SS_NOTHROW int sslen(const std::wstring& s)\r
1020 {\r
1021         return s.length();\r
1022 }\r
1023 \r
1024 // -----------------------------------------------------------------------------\r
1025 // sstolower/sstoupper -- convert characters to upper/lower case\r
1026 // -----------------------------------------------------------------------------\r
1027 template<typename CT>\r
1028 inline CT sstolower(const CT& t, const std::locale& loc = std::locale())\r
1029 {\r
1030         return std::tolower<CT>(t, loc);\r
1031 }\r
1032 template<typename CT>\r
1033 inline CT sstoupper(const CT& t, const std::locale& loc = std::locale())\r
1034 {\r
1035         return std::toupper<CT>(t, loc);\r
1036 }\r
1037 \r
1038 // -----------------------------------------------------------------------------\r
1039 // ssasn: assignment functions -- assign "sSrc" to "sDst"\r
1040 // -----------------------------------------------------------------------------\r
1041 typedef std::string::size_type          SS_SIZETYPE; // just for shorthand, really\r
1042 typedef std::string::pointer            SS_PTRTYPE;  \r
1043 typedef std::wstring::size_type         SW_SIZETYPE;\r
1044 typedef std::wstring::pointer           SW_PTRTYPE;  \r
1045 \r
1046 inline void     ssasn(std::string& sDst, const std::string& sSrc)\r
1047 {\r
1048         if ( sDst.c_str() != sSrc.c_str() )\r
1049         {\r
1050                 sDst.erase();\r
1051                 sDst.assign(SSREF(sSrc));\r
1052         }\r
1053 }\r
1054 inline void     ssasn(std::string& sDst, PCSTR pA)\r
1055 {\r
1056         // Watch out for NULLs, as always.\r
1057 \r
1058         if ( 0 == pA )\r
1059         {\r
1060                 sDst.erase();\r
1061         }\r
1062 \r
1063         // If pA actually points to part of sDst, we must NOT erase(), but\r
1064         // rather take a substring\r
1065 \r
1066         else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )\r
1067         {\r
1068                 sDst =sDst.substr(static_cast<SS_SIZETYPE>(pA-sDst.c_str()));\r
1069         }\r
1070 \r
1071         // Otherwise (most cases) apply the assignment bug fix, if applicable\r
1072         // and do the assignment\r
1073 \r
1074         else\r
1075         {\r
1076                 Q172398(sDst);\r
1077                 sDst.assign(pA);\r
1078         }\r
1079 }\r
1080 inline void     ssasn(std::string& sDst, const std::wstring& sSrc)\r
1081 {\r
1082         int nLen        = sSrc.size();\r
1083         sDst.resize(nLen * sizeof(wchar_t) + 1);\r
1084         StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), sSrc.c_str(), nLen);\r
1085         sDst.resize(nLen);\r
1086         //sDst.resize(sslen(sDst.c_str()));\r
1087 }\r
1088 inline void     ssasn(std::string& sDst, PCWSTR pW)\r
1089 {\r
1090         int nLen        = sslen(pW);\r
1091         sDst.resize(nLen * sizeof(wchar_t) + 1);\r
1092         StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), pW, nLen);\r
1093         sDst.resize(nLen);\r
1094         //sDst.resize(sslen(sDst.c_str()));\r
1095 }\r
1096 inline void ssasn(std::string& sDst, const int nNull)\r
1097 {\r
1098         UNUSED(nNull);\r
1099         ASSERT(nNull==0);\r
1100         sDst.assign("");\r
1101 }       \r
1102 inline void     ssasn(std::wstring& sDst, const std::wstring& sSrc)\r
1103 {\r
1104         if ( sDst.c_str() != sSrc.c_str() )\r
1105         {\r
1106                 sDst.erase();\r
1107                 sDst.assign(SSREF(sSrc));\r
1108         }\r
1109 }\r
1110 inline void     ssasn(std::wstring& sDst, PCWSTR pW)\r
1111 {\r
1112         // Watch out for NULLs, as always.\r
1113 \r
1114         if ( 0 == pW )\r
1115         {\r
1116                 sDst.erase();\r
1117         }\r
1118 \r
1119         // If pW actually points to part of sDst, we must NOT erase(), but\r
1120         // rather take a substring\r
1121 \r
1122         else if ( pW >= sDst.c_str() && pW <= sDst.c_str() + sDst.size() )\r
1123         {\r
1124                 sDst = sDst.substr(static_cast<SW_SIZETYPE>(pW-sDst.c_str()));\r
1125         }\r
1126 \r
1127         // Otherwise (most cases) apply the assignment bug fix, if applicable\r
1128         // and do the assignment\r
1129 \r
1130         else\r
1131         {\r
1132                 Q172398(sDst);\r
1133                 sDst.assign(pW);\r
1134         }\r
1135 }\r
1136 #undef StrSizeType\r
1137 inline void     ssasn(std::wstring& sDst, const std::string& sSrc)\r
1138 {\r
1139         int nLen        = sSrc.size();\r
1140         sDst.resize(nLen+1);\r
1141         StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), sSrc.c_str(), nLen+1);\r
1142         sDst.resize(sslen(sDst.c_str()));\r
1143 }\r
1144 inline void     ssasn(std::wstring& sDst, PCSTR pA)\r
1145 {\r
1146         int nLen        = sslen(pA);\r
1147         sDst.resize(nLen+1);\r
1148         StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), pA, nLen+1);\r
1149         sDst.resize(sslen(sDst.c_str()));\r
1150 }\r
1151 inline void ssasn(std::wstring& sDst, const int nNull)\r
1152 {\r
1153         UNUSED(nNull);\r
1154         ASSERT(nNull==0);\r
1155         sDst.assign(L"");\r
1156 }\r
1157 \r
1158 \r
1159 // -----------------------------------------------------------------------------\r
1160 // ssadd: string object concatenation -- add second argument to first\r
1161 // -----------------------------------------------------------------------------\r
1162 inline void     ssadd(std::string& sDst, const std::wstring& sSrc)\r
1163 {\r
1164         int nSrcLen     = sSrc.size();\r
1165         int nDstLen     = sDst.size();\r
1166         int nEndLen     = nSrcLen + nDstLen;\r
1167         sDst.resize(nEndLen + 1);\r
1168         StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDstLen), sSrc.c_str(), nSrcLen);\r
1169         sDst.resize(nEndLen);\r
1170 }\r
1171 inline void     ssadd(std::string& sDst, const std::string& sSrc)\r
1172 {\r
1173         if ( &sDst == &sSrc )\r
1174                 sDst.reserve(2*sDst.size());\r
1175 \r
1176         sDst.append(sSrc.c_str());\r
1177 }\r
1178 inline void     ssadd(std::string& sDst, PCWSTR pW)\r
1179 {\r
1180         int nSrcLen     = sslen(pW);\r
1181         int nDstLen     = sDst.size();\r
1182         int nEndLen     = nSrcLen + nDstLen;\r
1183         sDst.resize(nEndLen + 1);\r
1184         StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDstLen), pW, nSrcLen+1);\r
1185         sDst.resize(nEndLen);\r
1186 }\r
1187 inline void     ssadd(std::string& sDst, PCSTR pA)\r
1188 {\r
1189         if ( pA )\r
1190         {\r
1191                 // If the string being added is our internal string or a part of our\r
1192                 // internal string, then we must NOT do any reallocation without\r
1193                 // first copying that string to another object (since we're using a\r
1194                 // direct pointer)\r
1195 \r
1196                 if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length())\r
1197                 {\r
1198                         if ( sDst.capacity() <= sDst.size()+sslen(pA) )\r
1199                                 sDst.append(std::string(pA));\r
1200                         else\r
1201                                 sDst.append(pA);\r
1202                 }\r
1203                 else\r
1204                 {\r
1205                         sDst.append(pA); \r
1206                 }\r
1207         }\r
1208 }\r
1209 inline void     ssadd(std::wstring& sDst, const std::wstring& sSrc)\r
1210 {\r
1211         if ( &sDst == &sSrc )\r
1212                 sDst.reserve(2*sDst.size());\r
1213 \r
1214         sDst.append(sSrc.c_str());\r
1215 }\r
1216 inline void     ssadd(std::wstring& sDst, const std::string& sSrc)\r
1217 {\r
1218         int nSrcLen     = sSrc.size();\r
1219         int nDstLen     = sDst.size();\r
1220         int nEndLen     = nSrcLen + nDstLen;\r
1221         sDst.resize(nEndLen+1);\r
1222         StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDstLen), sSrc.c_str(), nSrcLen+1);\r
1223         sDst.resize(nEndLen);\r
1224 }\r
1225 inline void     ssadd(std::wstring& sDst, PCSTR pA)\r
1226 {\r
1227         int nSrcLen     = sslen(pA);\r
1228         int nDstLen     = sDst.size();\r
1229         int nEndLen     = nSrcLen + nDstLen;\r
1230         sDst.resize(nEndLen + 1);\r
1231         StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDstLen), pA, nSrcLen+1);\r
1232         sDst.resize(nEndLen);\r
1233 }\r
1234 inline void     ssadd(std::wstring& sDst, PCWSTR pW)\r
1235 {\r
1236         if ( pW )\r
1237         {\r
1238                 // If the string being added is our internal string or a part of our\r
1239                 // internal string, then we must NOT do any reallocation without\r
1240                 // first copying that string to another object (since we're using a\r
1241                 // direct pointer)\r
1242 \r
1243                 if ( pW >= sDst.c_str() && pW <= sDst.c_str()+sDst.length())\r
1244                 {\r
1245                         if ( sDst.capacity() <= sDst.size()+sslen(pW) )\r
1246                                 sDst.append(std::wstring(pW));\r
1247                         else\r
1248                                 sDst.append(pW);\r
1249                 }\r
1250                 else\r
1251                 {\r
1252                         sDst.append(pW);\r
1253                 }\r
1254         }\r
1255 }\r
1256 \r
1257 \r
1258 // -----------------------------------------------------------------------------\r
1259 // ssicmp: comparison (case insensitive )\r
1260 // -----------------------------------------------------------------------------\r
1261 template<typename CT>\r
1262 inline int ssicmp(const CT* pA1, const CT* pA2)\r
1263 {\r
1264     std::locale loc;\r
1265     const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);\r
1266     CT f;\r
1267     CT l;\r
1268 \r
1269     do \r
1270     {\r
1271             f = ct.tolower(*(pA1++));\r
1272             l = ct.tolower(*(pA2++));\r
1273     } while ( (f) && (f == l) );\r
1274 \r
1275     return (int)(f - l);\r
1276 }\r
1277 \r
1278 // -----------------------------------------------------------------------------\r
1279 // ssupr/sslwr: Uppercase/Lowercase conversion functions\r
1280 // -----------------------------------------------------------------------------\r
1281 \r
1282 template<typename CT>\r
1283 inline void sslwr(CT* pT, size_t nLen)\r
1284 {\r
1285         SS_USE_FACET(std::locale(), std::ctype<CT>).tolower(pT, pT+nLen);\r
1286 }\r
1287 template<typename CT>\r
1288 inline void ssupr(CT* pT, size_t nLen)\r
1289 {\r
1290         SS_USE_FACET(std::locale(), std::ctype<CT>).toupper(pT, pT+nLen);\r
1291 }\r
1292 \r
1293 \r
1294 // -----------------------------------------------------------------------------\r
1295 //  vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents.  In standard\r
1296 //  builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.\r
1297 // -----------------------------------------------------------------------------\r
1298 #if defined(SS_ANSI) || !defined(_MSC_VER)\r
1299 \r
1300     // Borland's headers put some ANSI "C" functions in the 'std' namespace.\r
1301     // Promote them to the global namespace so we can use them here.\r
1302 \r
1303     #if defined(__BORLANDC__)\r
1304         using std::vsprintf;\r
1305         using std::vswprintf;\r
1306     #endif\r
1307         inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)\r
1308         {\r
1309                 return vsprintf(pA, pFmtA, vl);\r
1310         }\r
1311         inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)\r
1312         {\r
1313                 // JMO: Some distributions of the "C" have a version of vswprintf that\r
1314         // takes 3 arguments (e.g. Microsoft, Borland, GNU).  Others have a \r
1315         // version which takes 4 arguments (an extra "count" argument in the\r
1316         // second position.  The best stab I can take at this so far is that if\r
1317         // you are NOT running with MS, Borland, or GNU, then I'll assume you\r
1318         // have the version that takes 4 arguments.\r
1319         //\r
1320         // I'm sure that these checks don't catch every platform correctly so if\r
1321         // you get compiler errors on one of the lines immediately below, it's\r
1322         // probably because your implemntation takes a different number of\r
1323         // arguments.  You can comment out the offending line (and use the\r
1324         // alternate version) or you can figure out what compiler flag to check\r
1325         // and add that preprocessor check in.  Regardless, if you get an error\r
1326         // on these lines, I'd sure like to hear from you about it.\r
1327         //\r
1328         // Thanks to Ronny Schulz for the SGI-specific checks here.\r
1329 \r
1330 //      #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)\r
1331     #if    !defined(_MSC_VER) \\r
1332         && !defined (__BORLANDC__) \\r
1333         && !defined(__GNUC__) \\r
1334         && !defined(__sgi)\r
1335 \r
1336         return vswprintf(pW, nCount, pFmtW, vl);\r
1337 \r
1338     // suddenly with the current SGI 7.3 compiler there is no such function as\r
1339     // vswprintf and the substitute needs explicit casts to compile\r
1340 \r
1341     #elif defined(__sgi)\r
1342 \r
1343         nCount;\r
1344         return vsprintf( (char *)pW, (char *)pFmtW, vl);\r
1345 \r
1346     #else\r
1347 \r
1348         nCount;\r
1349         return vswprintf(pW, pFmtW, vl);\r
1350 \r
1351     #endif\r
1352 \r
1353         }\r
1354 #else\r
1355         inline int      ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)\r
1356         {\r
1357                 return _vsnprintf(pA, nCount, pFmtA, vl);\r
1358         }\r
1359         inline int      ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)\r
1360         {\r
1361                 return _vsnwprintf(pW, nCount, pFmtW, vl);\r
1362         }\r
1363 #endif\r
1364 \r
1365 \r
1366 \r
1367 // -----------------------------------------------------------------------------\r
1368 // ssload: Type safe, overloaded ::LoadString wrappers\r
1369 // There is no equivalent of these in non-Win32-specific builds.  However, I'm\r
1370 // thinking that with the message facet, there might eventually be one\r
1371 // -----------------------------------------------------------------------------\r
1372 #if defined (SS_WIN32) && !defined(SS_ANSI)\r
1373         inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)\r
1374         {\r
1375                 return ::LoadStringA(hInst, uId, pBuf, nMax);\r
1376         }\r
1377         inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)\r
1378         {\r
1379                 return ::LoadStringW(hInst, uId, pBuf, nMax);\r
1380         }\r
1381 #endif\r
1382 \r
1383 \r
1384 // -----------------------------------------------------------------------------\r
1385 // sscoll/ssicoll: Collation wrappers\r
1386 //              Note -- with MSVC I have reversed the arguments order here because the\r
1387 //              functions appear to return the opposite of what they should\r
1388 // -----------------------------------------------------------------------------\r
1389 template <typename CT>\r
1390 inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)\r
1391 {\r
1392         const std::collate<CT>& coll =\r
1393                 SS_USE_FACET(std::locale(), std::collate<CT>);\r
1394 \r
1395         return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1);\r
1396 }\r
1397 template <typename CT>\r
1398 inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)\r
1399 {\r
1400         const std::locale loc;\r
1401         const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);\r
1402 \r
1403         // Some implementations seem to have trouble using the collate<>\r
1404         // facet typedefs so we'll just default to basic_string and hope\r
1405         // that's what the collate facet uses (which it generally should)\r
1406 \r
1407 //      std::collate<CT>::string_type s1(sz1);\r
1408 //      std::collate<CT>::string_type s2(sz2);\r
1409         const std::basic_string<CT> sEmpty;\r
1410     std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str());\r
1411     std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str());\r
1412 \r
1413         sslwr(const_cast<CT*>(s1.c_str()), nLen1);\r
1414         sslwr(const_cast<CT*>(s2.c_str()), nLen2);\r
1415         return coll.compare(s2.c_str(), s2.c_str()+nLen2,\r
1416                                                 s1.c_str(), s1.c_str()+nLen1);\r
1417 }\r
1418 \r
1419 \r
1420 // -----------------------------------------------------------------------------\r
1421 // ssfmtmsg: FormatMessage equivalents.  Needed because I added a CString facade\r
1422 // Again -- no equivalent of these on non-Win32 builds but their might one day\r
1423 // be one if the message facet gets implemented\r
1424 // -----------------------------------------------------------------------------\r
1425 #if defined (SS_WIN32) && !defined(SS_ANSI)\r
1426         inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,\r
1427                                                   DWORD dwLangId, PSTR pBuf, DWORD nSize,\r
1428                                                   va_list* vlArgs)\r
1429         { \r
1430                 return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,\r
1431                                                           pBuf, nSize,vlArgs);\r
1432         }\r
1433         inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,\r
1434                                                   DWORD dwLangId, PWSTR pBuf, DWORD nSize,\r
1435                                                   va_list* vlArgs)\r
1436         {\r
1437                 return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,\r
1438                                                           pBuf, nSize,vlArgs);\r
1439         }\r
1440 #else\r
1441 #endif\r
1442  \r
1443 \r
1444 \r
1445 // FUNCTION: sscpy.  Copies up to 'nMax' characters from pSrc to pDst.\r
1446 // -----------------------------------------------------------------------------\r
1447 // FUNCTION:  sscpy\r
1448 //              inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);\r
1449 //              inline int sscpy(PUSTR pDst,  PCSTR pSrc, int nMax=-1)\r
1450 //              inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);\r
1451 //              inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);\r
1452 //              inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);\r
1453 //\r
1454 // DESCRIPTION:\r
1455 //              This function is very much (but not exactly) like strcpy.  These\r
1456 //              overloads simplify copying one C-style string into another by allowing\r
1457 //              the caller to specify two different types of strings if necessary.\r
1458 //\r
1459 //              The strings must NOT overlap\r
1460 //\r
1461 //              "Character" is expressed in terms of the destination string, not\r
1462 //              the source.  If no 'nMax' argument is supplied, then the number of\r
1463 //              characters copied will be sslen(pSrc).  A NULL terminator will\r
1464 //              also be added so pDst must actually be big enough to hold nMax+1\r
1465 //              characters.  The return value is the number of characters copied,\r
1466 //              not including the NULL terminator.\r
1467 //\r
1468 // PARAMETERS: \r
1469 //              pSrc - the string to be copied FROM.  May be a char based string, an\r
1470 //                         MBCS string (in Win32 builds) or a wide string (wchar_t).\r
1471 //              pSrc - the string to be copied TO.  Also may be either MBCS or wide\r
1472 //              nMax - the maximum number of characters to be copied into szDest.  Note\r
1473 //                         that this is expressed in whatever a "character" means to pDst.\r
1474 //                         If pDst is a wchar_t type string than this will be the maximum\r
1475 //                         number of wchar_ts that my be copied.  The pDst string must be\r
1476 //                         large enough to hold least nMaxChars+1 characters.\r
1477 //                         If the caller supplies no argument for nMax this is a signal to\r
1478 //                         the routine to copy all the characters in pSrc, regardless of\r
1479 //                         how long it is.\r
1480 //\r
1481 // RETURN VALUE: none\r
1482 // -----------------------------------------------------------------------------\r
1483 template<typename CT1, typename CT2>\r
1484 inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nChars)\r
1485 {\r
1486         StdCodeCvt(pDst, pSrc, nChars);\r
1487         pDst[SSMAX(nChars, 0)]  = '\0';\r
1488         return nChars;\r
1489 }\r
1490 \r
1491 template<typename CT1, typename CT2>\r
1492 inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)\r
1493 {\r
1494         return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));\r
1495 }\r
1496 template<typename CT1, typename CT2>\r
1497 inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)\r
1498 {\r
1499         return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));\r
1500 }\r
1501 template<typename CT1, typename CT2>\r
1502 inline int sscpy(CT1* pDst, const CT2* pSrc)\r
1503 {\r
1504         return sscpycvt(pDst, pSrc, sslen(pSrc));\r
1505 }\r
1506 template<typename CT1, typename CT2>\r
1507 inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)\r
1508 {\r
1509         return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));\r
1510 }\r
1511 template<typename CT1, typename CT2>\r
1512 inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)\r
1513 {\r
1514         return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());\r
1515 }\r
1516 \r
1517 #ifdef SS_INC_COMDEF\r
1518         template<typename CT1>\r
1519         inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)\r
1520         {\r
1521                 return sscpycvt(pDst, static_cast<PCOLESTR>(bs),\r
1522             SSMIN(nMax, static_cast<int>(bs.length())));\r
1523         }\r
1524         template<typename CT1>\r
1525         inline int sscpy(CT1* pDst, const _bstr_t& bs)\r
1526         {\r
1527                 return sscpy(pDst, bs, static_cast<int>(bs.length()));\r
1528         }\r
1529 #endif\r
1530 \r
1531 \r
1532 // -----------------------------------------------------------------------------\r
1533 // Functional objects for changing case.  They also let you pass locales\r
1534 // -----------------------------------------------------------------------------\r
1535 \r
1536 #ifdef SS_ANSI\r
1537     template<typename CT>\r
1538     struct SSToUpper : public std::binary_function<CT, std::locale, CT>\r
1539     {\r
1540             inline CT operator()(const CT& t, const std::locale& loc) const\r
1541             {\r
1542                     return sstoupper<CT>(t, loc);\r
1543             }\r
1544     };\r
1545     template<typename CT>\r
1546     struct SSToLower : public std::binary_function<CT, std::locale, CT>\r
1547     {\r
1548             inline CT operator()(const CT& t, const std::locale& loc) const\r
1549             {\r
1550                     return sstolower<CT>(t, loc);\r
1551             }\r
1552     };\r
1553 #endif\r
1554 \r
1555 // This struct is used for TrimRight() and TrimLeft() function implementations.\r
1556 //template<typename CT>\r
1557 //struct NotSpace : public std::unary_function<CT, bool>\r
1558 //{\r
1559 //      const std::locale& loc;\r
1560 //      inline NotSpace(const std::locale& locArg) : loc(locArg) {}\r
1561 //      inline bool operator() (CT t) { return !std::isspace(t, loc); }\r
1562 //};\r
1563 template<typename CT>\r
1564 struct NotSpace : public std::unary_function<CT, bool>\r
1565 {\r
1566 \r
1567         // DINKUMWARE BUG:\r
1568         // Note -- using std::isspace in a COM DLL gives us access violations\r
1569         // because it causes the dynamic addition of a function to be called\r
1570         // when the library shuts down.  Unfortunately the list is maintained\r
1571         // in DLL memory but the function is in static memory.  So the COM DLL\r
1572         // goes away along with the function that was supposed to be called,\r
1573         // and then later when the DLL CRT shuts down it unloads the list and\r
1574         // tries to call the long-gone function.\r
1575         // This is DinkumWare's implementation problem.  Until then, we will\r
1576         // use good old isspace and iswspace from the CRT unless they\r
1577         // specify SS_ANSI\r
1578     \r
1579         const std::locale loc;\r
1580         NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {}\r
1581         bool operator() (CT t) const { return !std::isspace(t, loc); }\r
1582 };\r
1583 \r
1584 \r
1585 \r
1586 \r
1587 //                      Now we can define the template (finally!)\r
1588 // =============================================================================\r
1589 // TEMPLATE: CStdStr\r
1590 //              template<typename CT> class CStdStr : public std::basic_string<CT>\r
1591 //\r
1592 // REMARKS:\r
1593 //              This template derives from basic_string<CT> and adds some MFC CString-\r
1594 //              like functionality\r
1595 //\r
1596 //              Basically, this is my attempt to make Standard C++ library strings as\r
1597 //              easy to use as the MFC CString class.\r
1598 //\r
1599 //              Note that although this is a template, it makes the assumption that the\r
1600 //              template argument (CT, the character type) is either char or wchar_t.  \r
1601 // =============================================================================\r
1602 \r
1603 //#define CStdStr _SS   // avoid compiler warning 4786\r
1604 \r
1605 //    template<typename ARG> ARG& FmtArg(ARG& arg)  { return arg; }\r
1606 //    PCSTR  FmtArg(const std::string& arg)  { return arg.c_str(); }\r
1607 //    PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); }\r
1608 \r
1609 template<typename ARG>\r
1610 struct FmtArg\r
1611 {\r
1612     explicit FmtArg(const ARG& arg) : a_(arg) {}\r
1613     const ARG& Val() const { return a_; }\r
1614     const ARG& a_;\r
1615 private:\r
1616     FmtArg& operator=(const FmtArg&) { return *this; }\r
1617 };\r
1618 \r
1619 template<typename CT>\r
1620 class CStdStr : public std::basic_string<CT>\r
1621 {\r
1622         // Typedefs for shorter names.  Using these names also appears to help\r
1623         // us avoid some ambiguities that otherwise arise on some platforms\r
1624 \r
1625         typedef typename std::basic_string<CT>          MYBASE;  // my base class\r
1626         typedef CStdStr<CT>                                                     MYTYPE;  // myself\r
1627         typedef typename MYBASE::const_pointer          PCMYSTR; // PCSTR or PCWSTR \r
1628         typedef typename MYBASE::pointer                        PMYSTR;  // PSTR or PWSTR\r
1629         typedef typename MYBASE::iterator                       MYITER;  // my iterator type\r
1630         typedef typename MYBASE::const_iterator         MYCITER; // you get the idea...\r
1631         typedef typename MYBASE::reverse_iterator       MYRITER;\r
1632         typedef typename MYBASE::size_type                      MYSIZE;   \r
1633         typedef typename MYBASE::value_type                     MYVAL; \r
1634         typedef typename MYBASE::allocator_type         MYALLOC;\r
1635         \r
1636 public:\r
1637 \r
1638         // shorthand conversion from PCTSTR to string resource ID\r
1639         #define _TRES(pctstr) (LOWORD((DWORD)(pctstr))) \r
1640 \r
1641         // CStdStr inline constructors\r
1642         CStdStr()\r
1643         {\r
1644         }\r
1645 \r
1646         CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))\r
1647         {\r
1648         }\r
1649 \r
1650         CStdStr(const std::string& str)\r
1651         {\r
1652                 ssasn(*this, SSREF(str));\r
1653         }\r
1654 \r
1655         CStdStr(const std::wstring& str)\r
1656         {\r
1657                 ssasn(*this, SSREF(str));\r
1658         }\r
1659 \r
1660         CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)\r
1661         {\r
1662         }\r
1663 \r
1664 #ifdef SS_UNSIGNED\r
1665         CStdStr(PCUSTR pU)\r
1666         {\r
1667                 *this = reinterpret_cast<PCSTR>(pU);\r
1668         }\r
1669 #endif\r
1670 \r
1671         CStdStr(PCSTR pA)\r
1672         {\r
1673         #ifdef SS_ANSI\r
1674                 *this = pA;\r
1675         #else\r
1676                 if ( 0 != HIWORD(pA) )\r
1677                         *this = pA;\r
1678                 else if ( 0 != pA && !Load(_TRES(pA)) )\r
1679                         TRACE(_T("Can't load string %u\n"), _TRES(pA));\r
1680         #endif\r
1681         }\r
1682 \r
1683         CStdStr(PCWSTR pW)\r
1684         {\r
1685         #ifdef SS_ANSI\r
1686                 *this = pW;\r
1687         #else\r
1688                 if ( 0 != HIWORD(pW) )\r
1689                         *this = pW;\r
1690                 else if ( 0 != pW && !Load(_TRES(pW)) )\r
1691                         TRACE(_T("Can't load string %u\n"), _TRES(pW));\r
1692         #endif\r
1693         }\r
1694 \r
1695         CStdStr(MYCITER first, MYCITER last)\r
1696                 : MYBASE(first, last)\r
1697         {\r
1698         }\r
1699 \r
1700         CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())\r
1701                 : MYBASE(nSize, ch, al)\r
1702         {\r
1703         }\r
1704 \r
1705         #ifdef SS_INC_COMDEF\r
1706                 CStdStr(const _bstr_t& bstr)\r
1707                 {\r
1708                         if ( bstr.length() > 0 )\r
1709                                 this->append(static_cast<PCMYSTR>(bstr), bstr.length());\r
1710                 }\r
1711         #endif\r
1712 \r
1713         // CStdStr inline assignment operators -- the ssasn function now takes care\r
1714         // of fixing  the MSVC assignment bug (see knowledge base article Q172398).\r
1715         MYTYPE& operator=(const MYTYPE& str)\r
1716         { \r
1717                 ssasn(*this, str); \r
1718                 return *this;\r
1719         }\r
1720 \r
1721         MYTYPE& operator=(const std::string& str)\r
1722         {\r
1723                 ssasn(*this, str);\r
1724                 return *this;\r
1725         }\r
1726 \r
1727         MYTYPE& operator=(const std::wstring& str)\r
1728         {\r
1729                 ssasn(*this, str);\r
1730                 return *this;\r
1731         }\r
1732 \r
1733         MYTYPE& operator=(PCSTR pA)\r
1734         {\r
1735                 ssasn(*this, pA);\r
1736                 return *this;\r
1737         }\r
1738 \r
1739         MYTYPE& operator=(PCWSTR pW)\r
1740         {\r
1741                 ssasn(*this, pW);\r
1742                 return *this;\r
1743         }\r
1744 \r
1745 #ifdef SS_UNSIGNED\r
1746         MYTYPE& operator=(PCUSTR pU)\r
1747         {\r
1748                 ssasn(*this, reinterpret_cast<PCSTR>(pU)):\r
1749                 return *this;\r
1750         }\r
1751 #endif\r
1752 \r
1753         MYTYPE& operator=(CT t)\r
1754         {\r
1755                 Q172398(*this);\r
1756                 this->assign(1, t);\r
1757                 return *this;\r
1758         }\r
1759 \r
1760         #ifdef SS_INC_COMDEF\r
1761                 MYTYPE& operator=(const _bstr_t& bstr)\r
1762                 {\r
1763                         if ( bstr.length() > 0 )\r
1764                         {\r
1765                                 this->assign(static_cast<PCMYSTR>(bstr), bstr.length());\r
1766                                 return *this;\r
1767                         }\r
1768                         else\r
1769                         {\r
1770                                 this->erase();\r
1771                                 return *this;\r
1772                         }\r
1773                 }\r
1774         #endif\r
1775 \r
1776 \r
1777         // Overloads  also needed to fix the MSVC assignment bug (KB: Q172398)\r
1778         //  *** Thanks to Pete The Plumber for catching this one ***\r
1779         // They also are compiled if you have explicitly turned off refcounting\r
1780         #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT) \r
1781 \r
1782                 MYTYPE& assign(const MYTYPE& str)\r
1783                 {\r
1784                         ssasn(*this, str);\r
1785                         return *this;\r
1786                 }\r
1787 \r
1788                 MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)\r
1789                 {\r
1790                         // This overload of basic_string::assign is supposed to assign up to\r
1791                         // <nChars> or the NULL terminator, whichever comes first.  Since we\r
1792                         // are about to call a less forgiving overload (in which <nChars>\r
1793                         // must be a valid length), we must adjust the length here to a safe\r
1794                         // value.  Thanks to Ullrich Pollähne for catching this bug\r
1795 \r
1796                         nChars          = SSMIN(nChars, str.length() - nStart);\r
1797 \r
1798                         // Watch out for assignment to self\r
1799 \r
1800                         if ( this == &str )\r
1801                         {\r
1802                                 MYTYPE strTemp(str.c_str()+nStart, nChars);\r
1803                                 MYBASE::assign(strTemp);\r
1804                         }\r
1805                         else\r
1806                         {\r
1807                                 Q172398(*this);\r
1808                                 MYBASE::assign(str.c_str()+nStart, nChars);\r
1809                         }\r
1810                         return *this;\r
1811                 }\r
1812 \r
1813                 MYTYPE& assign(const MYBASE& str)\r
1814                 {\r
1815                         ssasn(*this, str);\r
1816                         return *this;\r
1817                 }\r
1818 \r
1819                 MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)\r
1820                 {\r
1821                         // This overload of basic_string::assign is supposed to assign up to\r
1822                         // <nChars> or the NULL terminator, whichever comes first.  Since we\r
1823                         // are about to call a less forgiving overload (in which <nChars>\r
1824                         // must be a valid length), we must adjust the length here to a safe\r
1825                         // value. Thanks to Ullrich Pollähne for catching this bug\r
1826 \r
1827                         nChars          = SSMIN(nChars, str.length() - nStart);\r
1828 \r
1829                         // Watch out for assignment to self\r
1830 \r
1831                         if ( this == &str )     // watch out for assignment to self\r
1832                         {\r
1833                                 MYTYPE strTemp(str.c_str() + nStart, nChars);\r
1834                                 MYBASE::assign(strTemp);\r
1835                         }\r
1836                         else\r
1837                         {\r
1838                                 Q172398(*this);\r
1839                                 MYBASE::assign(str.c_str()+nStart, nChars);\r
1840                         }\r
1841                         return *this;\r
1842                 }\r
1843 \r
1844                 MYTYPE& assign(const CT* pC, MYSIZE nChars)\r
1845                 {\r
1846                         // Q172398 only fix -- erase before assigning, but not if we're\r
1847                         // assigning from our own buffer\r
1848 \r
1849         #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )\r
1850                         if ( !this->empty() &&\r
1851                                 ( pC < this->data() || pC > this->data() + this->capacity() ) )\r
1852                         {\r
1853                                 this->erase();\r
1854                         }\r
1855         #endif\r
1856                         Q172398(*this);\r
1857                         MYBASE::assign(pC, nChars);\r
1858                         return *this;\r
1859                 }\r
1860 \r
1861                 MYTYPE& assign(MYSIZE nChars, MYVAL val)\r
1862                 {\r
1863                         Q172398(*this);\r
1864                         MYBASE::assign(nChars, val);\r
1865                         return *this;\r
1866                 }\r
1867 \r
1868                 MYTYPE& assign(const CT* pT)\r
1869                 {\r
1870                         return this->assign(pT, MYBASE::traits_type::length(pT));\r
1871                 }\r
1872 \r
1873                 MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)\r
1874                 {\r
1875         #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 ) \r
1876                         // Q172398 fix.  don't call erase() if we're assigning from ourself\r
1877                         if ( iterFirst < this->begin() ||\r
1878                  iterFirst > this->begin() + this->size() )\r
1879             {\r
1880                                 this->erase()\r
1881             }\r
1882         #endif\r
1883                         this->replace(this->begin(), this->end(), iterFirst, iterLast);\r
1884                         return *this;\r
1885                 }\r
1886         #endif\r
1887 \r
1888 \r
1889         // -------------------------------------------------------------------------\r
1890         // CStdStr inline concatenation.\r
1891         // -------------------------------------------------------------------------\r
1892         MYTYPE& operator+=(const MYTYPE& str)\r
1893         {\r
1894                 ssadd(*this, str);\r
1895                 return *this;\r
1896         }\r
1897 \r
1898         MYTYPE& operator+=(const std::string& str)\r
1899         {\r
1900                 ssadd(*this, str);\r
1901                 return *this; \r
1902         }\r
1903 \r
1904         MYTYPE& operator+=(const std::wstring& str)\r
1905         {\r
1906                 ssadd(*this, str);\r
1907                 return *this;\r
1908         }\r
1909 \r
1910         MYTYPE& operator+=(PCSTR pA)\r
1911         {\r
1912                 ssadd(*this, pA);\r
1913                 return *this;\r
1914         }\r
1915 \r
1916         MYTYPE& operator+=(PCWSTR pW)\r
1917         {\r
1918                 ssadd(*this, pW);\r
1919                 return *this;\r
1920         }\r
1921 \r
1922         MYTYPE& operator+=(CT t)\r
1923         {\r
1924                 this->append(1, t);\r
1925                 return *this;\r
1926         }\r
1927         #ifdef SS_INC_COMDEF    // if we have _bstr_t, define a += for it too.\r
1928                 MYTYPE& operator+=(const _bstr_t& bstr)\r
1929                 {\r
1930                         return this->operator+=(static_cast<PCMYSTR>(bstr));\r
1931                 }\r
1932         #endif\r
1933 \r
1934 \r
1935         // -------------------------------------------------------------------------\r
1936         // Case changing functions\r
1937         // -------------------------------------------------------------------------\r
1938 \r
1939     MYTYPE& ToUpper()\r
1940         {\r
1941                 //  Strictly speaking, this would be about the most portable way\r
1942 \r
1943                 //      std::transform(begin(),\r
1944                 //                                 end(),\r
1945                 //                                 begin(),\r
1946                 //                                 std::bind2nd(SSToUpper<CT>(), std::locale()));\r
1947 \r
1948                 // But practically speaking, this works faster\r
1949 \r
1950                 if ( !empty() )\r
1951                         ssupr(GetBuf(), this->size());\r
1952 \r
1953                 return *this;\r
1954         }\r
1955 \r
1956 \r
1957 \r
1958         MYTYPE& ToLower()\r
1959         {\r
1960                 //  Strictly speaking, this would be about the most portable way\r
1961 \r
1962                 //      std::transform(begin(),\r
1963                 //                                 end(),\r
1964                 //                                 begin(),\r
1965                 //                                 std::bind2nd(SSToLower<CT>(), std::locale()));\r
1966 \r
1967                 // But practically speaking, this works faster\r
1968 \r
1969                 if ( !empty() )\r
1970                         sslwr(GetBuf(), this->size());\r
1971 \r
1972                 return *this;\r
1973         }\r
1974 \r
1975 \r
1976 \r
1977         MYTYPE& Normalize()\r
1978         {\r
1979                 return Trim().ToLower();\r
1980         }\r
1981 \r
1982 \r
1983         // -------------------------------------------------------------------------\r
1984         // CStdStr -- Direct access to character buffer.  In the MS' implementation,\r
1985         // the at() function that we use here also calls _Freeze() providing us some\r
1986         // protection from multithreading problems associated with ref-counting.\r
1987     // In VC 7 and later, of course, the ref-counting stuff is gone.\r
1988         // -------------------------------------------------------------------------\r
1989 \r
1990         CT* GetBuf(int nMinLen=-1)\r
1991         {\r
1992                 if ( static_cast<int>(size()) < nMinLen )\r
1993                         this->resize(static_cast<MYSIZE>(nMinLen));\r
1994 \r
1995                 return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));\r
1996         }\r
1997 \r
1998         CT* SetBuf(int nLen)\r
1999         {\r
2000                 nLen = ( nLen > 0 ? nLen : 0 );\r
2001                 if ( this->capacity() < 1 && nLen == 0 )\r
2002                         this->resize(1);\r
2003 \r
2004                 this->resize(static_cast<MYSIZE>(nLen));\r
2005                 return const_cast<CT*>(this->data());\r
2006         }\r
2007         void RelBuf(int nNewLen=-1)\r
2008         {\r
2009                 this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :\r
2010                                                         sslen(this->c_str())));\r
2011         }\r
2012 \r
2013         void BufferRel()                 { RelBuf(); }                  // backwards compatability\r
2014         CT*  Buffer()                    { return GetBuf(); }   // backwards compatability\r
2015         CT*  BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability\r
2016 \r
2017         bool Equals(const CT* pT, bool bUseCase=false) const\r
2018         {       // get copy, THEN compare (thread safe)\r
2019                 return  bUseCase ? this->compare(pT) == 0 :\r
2020                            ssicmp(MYTYPE(*this).c_str(), pT) == 0;\r
2021         } \r
2022 \r
2023         // -------------------------------------------------------------------------\r
2024         // FUNCTION:  CStdStr::Load\r
2025         // REMARKS:\r
2026         //              Loads string from resource specified by nID\r
2027         //\r
2028         // PARAMETERS:\r
2029         //              nID - resource Identifier.  Purely a Win32 thing in this case\r
2030         //\r
2031         // RETURN VALUE:\r
2032         //              true if successful, false otherwise\r
2033         // -------------------------------------------------------------------------\r
2034 \r
2035 #ifndef SS_ANSI\r
2036 \r
2037         bool Load(UINT nId, HMODULE hModule=NULL)\r
2038         {\r
2039                 bool bLoaded            = false;        // set to true of we succeed.\r
2040 \r
2041         #ifdef _MFC_VER         // When in Rome (or MFC land)...\r
2042 \r
2043                 CString strRes;\r
2044                 bLoaded                         = FALSE != strRes.LoadString(nId);\r
2045                 if ( bLoaded )\r
2046                         *this                   = strRes;\r
2047 \r
2048         #else // otherwise make our own hackneyed version of CString's Load\r
2049                 \r
2050                 // Get the resource name and module handle\r
2051 \r
2052                 if ( NULL == hModule )\r
2053                         hModule                 = GetResourceHandle();\r
2054 \r
2055                 PCTSTR szName           = MAKEINTRESOURCE((nId>>4)+1); // lifted \r
2056                 DWORD dwSize            = 0;\r
2057 \r
2058                 // No sense continuing if we can't find the resource\r
2059 \r
2060                 HRSRC hrsrc                     = ::FindResource(hModule, szName, RT_STRING);\r
2061 \r
2062                 if ( NULL == hrsrc )\r
2063                 {\r
2064                         TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());\r
2065                 }\r
2066                 else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))\r
2067                 {\r
2068                         TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());\r
2069                 }\r
2070                 else\r
2071                 {\r
2072                         bLoaded                 = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);\r
2073                         ReleaseBuffer();\r
2074                 }\r
2075 \r
2076         #endif  // #ifdef _MFC_VER\r
2077 \r
2078                 if ( !bLoaded )\r
2079                         TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());\r
2080 \r
2081                 return bLoaded;\r
2082         }\r
2083 \r
2084 #endif  // #ifdef SS_ANSI\r
2085         \r
2086         // -------------------------------------------------------------------------\r
2087         // FUNCTION:  CStdStr::Format\r
2088         //              void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)\r
2089         //              void _cdecl Format(PCSTR szFormat);\r
2090         //           \r
2091         // DESCRIPTION:\r
2092         //              This function does sprintf/wsprintf style formatting on CStdStringA\r
2093         //              objects.  It looks a lot like MFC's CString::Format.  Some people\r
2094         //              might even call this identical.  Fortunately, these people are now\r
2095         //              dead... heh heh.\r
2096         //\r
2097         // PARAMETERS: \r
2098         //              nId - ID of string resource holding the format string\r
2099         //              szFormat - a PCSTR holding the format specifiers\r
2100         //              argList - a va_list holding the arguments for the format specifiers.\r
2101         //\r
2102         // RETURN VALUE:  None.\r
2103         // -------------------------------------------------------------------------\r
2104         // formatting (using wsprintf style formatting)\r
2105 \r
2106     // If they want a Format() function that safely handles string objects\r
2107     // without casting\r
2108  \r
2109 #ifdef SS_SAFE_FORMAT       \r
2110     \r
2111     // Question:  Joe, you wacky coder you, why do you have so many overloads\r
2112     //      of the Format() function\r
2113     // Answer:  One reason only - CString compatability.  In short, by making\r
2114     //      the Format() function a template this way, I can do strong typing\r
2115     //      and allow people to pass CStdString arguments as fillers for\r
2116     //      "%s" format specifiers without crashing their program!  The downside\r
2117     //      is that I need to overload on the number of arguments.   If you are\r
2118     //      passing more arguments than I have listed below in any of my\r
2119     //      overloads, just add another one.\r
2120     //\r
2121     //      Yes, yes, this is really ugly.  In essence what I am doing here is\r
2122     //      protecting people from a bad (and incorrect) programming practice\r
2123     //      that they should not be doing anyway.  I am protecting them from\r
2124     //      themselves.  Why am I doing this?  Well, if you had any idea the\r
2125     //      number of times I've been emailed by people about this\r
2126     //      "incompatability" in my code, you wouldn't ask.\r
2127 \r
2128         void Fmt(const CT* szFmt, ...)\r
2129         {\r
2130                 va_list argList;\r
2131                 va_start(argList, szFmt);\r
2132                 FormatV(szFmt, argList);\r
2133                 va_end(argList);\r
2134         }\r
2135 \r
2136 #ifndef SS_ANSI\r
2137 \r
2138     void Format(UINT nId)\r
2139     {\r
2140                 MYTYPE strFmt;\r
2141                 if ( strFmt.Load(nId) ) \r
2142             this->swap(strFmt);\r
2143     }\r
2144     template<class A1>\r
2145     void Format(UINT nId, const A1& v)\r
2146     {\r
2147                 MYTYPE strFmt;\r
2148                 if ( strFmt.Load(nId) )\r
2149             Fmt(strFmt, FmtArg<A1>(v).Val());\r
2150     }\r
2151     template<class A1, class A2>\r
2152     void Format(UINT nId, const A1& v1, const A2& v2)\r
2153     {\r
2154                 MYTYPE strFmt;\r
2155                 if ( strFmt.Load(nId) )\r
2156            Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val());\r
2157     }\r
2158     template<class A1, class A2, class A3>\r
2159     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3)\r
2160     {\r
2161                 MYTYPE strFmt;\r
2162                 if ( strFmt.Load(nId) )\r
2163         {\r
2164             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2165             FmtArg<A3>(v3).Val());\r
2166         }\r
2167     }\r
2168     template<class A1, class A2, class A3, class A4>\r
2169     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2170                 const A4& v4)\r
2171     {\r
2172                 MYTYPE strFmt;\r
2173                 if ( strFmt.Load(nId) )\r
2174         {\r
2175             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2176                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val());\r
2177         }\r
2178     }\r
2179     template<class A1, class A2, class A3, class A4, class A5>\r
2180     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2181                 const A4& v4, const A5& v5)\r
2182     {\r
2183                 MYTYPE strFmt;\r
2184                 if ( strFmt.Load(nId) )\r
2185         {\r
2186             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2187                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val());\r
2188         }\r
2189     }\r
2190     template<class A1, class A2, class A3, class A4, class A5, class A6>\r
2191     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2192                 const A4& v4, const A5& v5, const A6& v6)\r
2193     {\r
2194                 MYTYPE strFmt;\r
2195                 if ( strFmt.Load(nId) )\r
2196         {\r
2197             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2198                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(),FmtArg<A5>(v5).Val(),\r
2199                 FmtArg<A6>(v6).Val());\r
2200         }\r
2201     }\r
2202     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2203         class A7>\r
2204     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2205                 const A4& v4, const A5& v5, const A6& v6, const A7& v7)\r
2206     {\r
2207                 MYTYPE strFmt;\r
2208                 if ( strFmt.Load(nId) )\r
2209         {\r
2210             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2211                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(),FmtArg<A5>(v5).Val(),\r
2212                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val());\r
2213         }\r
2214     }\r
2215     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2216         class A7, class A8>\r
2217     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2218                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2219                 const A8& v8)\r
2220     {\r
2221                 MYTYPE strFmt;\r
2222                 if ( strFmt.Load(nId) )\r
2223         {\r
2224            Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2225                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2226                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val());\r
2227         }\r
2228     }\r
2229     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2230         class A7, class A8, class A9>\r
2231     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2232                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2233                 const A8& v8, const A9& v9)\r
2234     {\r
2235                 MYTYPE strFmt;\r
2236                 if ( strFmt.Load(nId) )\r
2237         {\r
2238             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2239                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2240                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2241                 FmtArg<A9>(v9).Val());\r
2242         }\r
2243     }\r
2244     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2245         class A7, class A8, class A9, class A10>\r
2246     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2247                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2248                 const A8& v8, const A9& v9, const A10& v10)\r
2249     {\r
2250                 MYTYPE strFmt;\r
2251                 if ( strFmt.Load(nId) )\r
2252         {\r
2253             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2254                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2255                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2256                 FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val());\r
2257         }\r
2258     }\r
2259     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2260         class A7, class A8, class A9, class A10, class A11>\r
2261     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2262                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2263                 const A8& v8, const A9& v9, const A10& v10, const A11& v11)\r
2264     {\r
2265                 MYTYPE strFmt;\r
2266                 if ( strFmt.Load(nId) )\r
2267         {\r
2268             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2269                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2270                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2271                 FmtArg<A9>(v9).Val(),FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val());\r
2272         }\r
2273     }\r
2274     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2275         class A7, class A8, class A9, class A10, class A11, class A12>\r
2276     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2277                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2278                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2279                 const A12& v12)\r
2280     {\r
2281                 MYTYPE strFmt;\r
2282                 if ( strFmt.Load(nId) )\r
2283         {\r
2284             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2285                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2286                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2287                 FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2288                 FmtArg<A12>(v12).Val());\r
2289         }\r
2290     }\r
2291     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2292         class A7, class A8, class A9, class A10, class A11, class A12,\r
2293         class A13>\r
2294     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2295                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2296                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2297                 const A12& v12, const A13& v13)\r
2298     {\r
2299                 MYTYPE strFmt;\r
2300                 if ( strFmt.Load(nId) )\r
2301         {\r
2302             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2303                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2304                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2305                 FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2306                 FmtArg<A12>(v12).Val(), FmtArg<A13>(v13).Val());\r
2307         }\r
2308     }\r
2309     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2310         class A7, class A8, class A9, class A10, class A11, class A12,\r
2311         class A13, class A14>\r
2312     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2313                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2314                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2315                 const A12& v12, const A13& v13, const A14& v14)\r
2316     {\r
2317                 MYTYPE strFmt;\r
2318                 if ( strFmt.Load(nId) )\r
2319         {\r
2320             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2321                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2322                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2323                 FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2324                 FmtArg<A12>(v12).Val(), FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val());\r
2325         }\r
2326     }\r
2327     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2328         class A7, class A8, class A9, class A10, class A11, class A12,\r
2329         class A13, class A14, class A15>\r
2330     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2331                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2332                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2333                 const A12& v12, const A13& v13, const A14& v14, const A15& v15)\r
2334     {\r
2335                 MYTYPE strFmt;\r
2336                 if ( strFmt.Load(nId) )\r
2337         {\r
2338             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2339                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2340                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2341                 FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2342                 FmtArg<A12>(v12).Val(),FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val(),\r
2343                 FmtArg<A15>(v15).Val());\r
2344         }\r
2345     }\r
2346     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2347         class A7, class A8, class A9, class A10, class A11, class A12,\r
2348         class A13, class A14, class A15, class A16>\r
2349     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2350                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2351                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2352                 const A12& v12, const A13& v13, const A14& v14, const A15& v15,\r
2353                 const A16& v16)\r
2354     {\r
2355                 MYTYPE strFmt;\r
2356                 if ( strFmt.Load(nId) )\r
2357         {\r
2358             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2359                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2360                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2361                 FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2362                 FmtArg<A12>(v12).Val(),FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val(),\r
2363                 FmtArg<A15>(v15).Val(), FmtArg<A16>(v16).Val());\r
2364         }\r
2365     }\r
2366     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2367         class A7, class A8, class A9, class A10, class A11, class A12,\r
2368         class A13, class A14, class A15, class A16, class A17>\r
2369     void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,\r
2370                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2371                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2372                 const A12& v12, const A13& v13, const A14& v14, const A15& v15,\r
2373                 const A16& v16, const A17& v17)\r
2374     {\r
2375                 MYTYPE strFmt;\r
2376                 if ( strFmt.Load(nId) )\r
2377         {\r
2378             Fmt(strFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2379                 FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2380                 FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2381                 FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2382                 FmtArg<A12>(v12).Val(),FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val(),\r
2383                 FmtArg<A15>(v15).Val(),FmtArg<A16>(v16).Val(),FmtArg<A17>(v17).Val());\r
2384         }\r
2385     }\r
2386     \r
2387 #endif // #ifndef SS_ANSI\r
2388 \r
2389     // ...now the other overload of Format: the one that takes a string literal\r
2390 \r
2391     void Format(const CT* szFmt)\r
2392     {\r
2393         *this = szFmt;\r
2394     }\r
2395     template<class A1>\r
2396     void Format(const CT* szFmt, A1 v)\r
2397     {\r
2398         Fmt(szFmt, FmtArg<A1>(v).Val());\r
2399     }\r
2400     template<class A1, class A2>\r
2401     void Format(const CT* szFmt, const A1& v1, const A2& v2)\r
2402     {\r
2403         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val());\r
2404     }\r
2405     template<class A1, class A2, class A3>\r
2406     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3)\r
2407     {\r
2408         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2409             FmtArg<A3>(v3).Val());\r
2410     }\r
2411     template<class A1, class A2, class A3, class A4>\r
2412     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2413                 const A4& v4)\r
2414     {\r
2415         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2416             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val());\r
2417     }\r
2418     template<class A1, class A2, class A3, class A4, class A5>\r
2419     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2420                 const A4& v4, const A5& v5)\r
2421     {\r
2422         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2423             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val());\r
2424     }\r
2425     template<class A1, class A2, class A3, class A4, class A5, class A6>\r
2426     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2427                 const A4& v4, const A5& v5, const A6& v6)\r
2428     {\r
2429         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2430             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2431             FmtArg<A6>(v6).Val());\r
2432     }\r
2433     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2434         class A7>\r
2435     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2436                 const A4& v4, const A5& v5, const A6& v6, const A7& v7)\r
2437     {\r
2438         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2439             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2440             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val());\r
2441     }\r
2442     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2443         class A7, class A8>\r
2444     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2445                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2446                 const A8& v8)\r
2447     {\r
2448         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2449             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2450             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val());\r
2451     }\r
2452     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2453         class A7, class A8, class A9>\r
2454     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2455                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2456                 const A8& v8, const A9& v9)\r
2457     {\r
2458         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2459             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2460             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2461             FmtArg<A9>(v9).Val());\r
2462     }\r
2463     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2464         class A7, class A8, class A9, class A10>\r
2465     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2466                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2467                 const A8& v8, const A9& v9, const A10& v10)\r
2468     {\r
2469         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2470             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2471             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2472             FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val());\r
2473     }\r
2474     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2475         class A7, class A8, class A9, class A10, class A11>\r
2476     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2477                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2478                 const A8& v8, const A9& v9, const A10& v10, const A11& v11)\r
2479     {\r
2480         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2481             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2482             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2483             FmtArg<A9>(v9).Val(),FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val());\r
2484     }\r
2485     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2486         class A7, class A8, class A9, class A10, class A11, class A12>\r
2487     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2488                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2489                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2490                 const A12& v12)\r
2491     {\r
2492         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2493             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2494             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2495             FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2496             FmtArg<A12>(v12).Val());\r
2497     }\r
2498     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2499         class A7, class A8, class A9, class A10, class A11, class A12,\r
2500         class A13>\r
2501     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2502                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2503                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2504                 const A12& v12, const A13& v13)\r
2505     {\r
2506         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2507             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2508             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2509             FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2510             FmtArg<A12>(v12).Val(), FmtArg<A13>(v13).Val());\r
2511     }\r
2512     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2513         class A7, class A8, class A9, class A10, class A11, class A12,\r
2514         class A13, class A14>\r
2515     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2516                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2517                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2518                 const A12& v12, const A13& v13, const A14& v14)\r
2519     {\r
2520         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2521             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2522             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2523             FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2524             FmtArg<A12>(v12).Val(), FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val());\r
2525     }\r
2526     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2527         class A7, class A8, class A9, class A10, class A11, class A12,\r
2528         class A13, class A14, class A15>\r
2529     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2530                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2531                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2532                 const A12& v12, const A13& v13, const A14& v14, const A15& v15)\r
2533     {\r
2534         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2535             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2536             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2537             FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2538             FmtArg<A12>(v12).Val(),FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val(),\r
2539             FmtArg<A15>(v15).Val());\r
2540     }\r
2541     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2542         class A7, class A8, class A9, class A10, class A11, class A12,\r
2543         class A13, class A14, class A15, class A16>\r
2544     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2545                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2546                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2547                 const A12& v12, const A13& v13, const A14& v14, const A15& v15,\r
2548                 const A16& v16)\r
2549     {\r
2550         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2551             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2552             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2553             FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2554             FmtArg<A12>(v12).Val(),FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val(),\r
2555             FmtArg<A15>(v15).Val(), FmtArg<A16>(v16).Val());\r
2556     }\r
2557     template<class A1, class A2, class A3, class A4, class A5, class A6,\r
2558         class A7, class A8, class A9, class A10, class A11, class A12,\r
2559         class A13, class A14, class A15, class A16, class A17>\r
2560     void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,\r
2561                 const A4& v4, const A5& v5, const A6& v6, const A7& v7,\r
2562                 const A8& v8, const A9& v9, const A10& v10, const A11& v11,\r
2563                 const A12& v12, const A13& v13, const A14& v14, const A15& v15,\r
2564                 const A16& v16, const A17& v17)\r
2565     {\r
2566         Fmt(szFmt, FmtArg<A1>(v1).Val(), FmtArg<A2>(v2).Val(),\r
2567             FmtArg<A3>(v3).Val(), FmtArg<A4>(v4).Val(), FmtArg<A5>(v5).Val(),\r
2568             FmtArg<A6>(v6).Val(), FmtArg<A7>(v7).Val(), FmtArg<A8>(v8).Val(),\r
2569             FmtArg<A9>(v9).Val(), FmtArg<A10>(v10).Val(),FmtArg<A11>(v11).Val(),\r
2570             FmtArg<A12>(v12).Val(),FmtArg<A13>(v13).Val(),FmtArg<A14>(v14).Val(),\r
2571             FmtArg<A15>(v15).Val(),FmtArg<A16>(v16).Val(),FmtArg<A17>(v17).Val());\r
2572     }\r
2573 \r
2574 #else  // #ifdef SS_SAFE_FORMAT\r
2575 \r
2576 \r
2577 #ifndef SS_ANSI\r
2578 \r
2579         void Format(UINT nId, ...)\r
2580         {\r
2581                 va_list argList;\r
2582                 va_start(argList, nId);\r
2583                 va_start(argList, nId);\r
2584 \r
2585                 MYTYPE strFmt;\r
2586                 if ( strFmt.Load(nId) )\r
2587                         FormatV(strFmt, argList);\r
2588 \r
2589                 va_end(argList);\r
2590         }\r
2591     \r
2592 #endif  // #ifdef SS_ANSI\r
2593 \r
2594         void Format(const CT* szFmt, ...)\r
2595         {\r
2596                 va_list argList;\r
2597                 va_start(argList, szFmt);\r
2598                 FormatV(szFmt, argList);\r
2599                 va_end(argList);\r
2600         }\r
2601 \r
2602 #endif // #ifdef SS_SAFE_FORMAT\r
2603 \r
2604         void AppendFormat(const CT* szFmt, ...)\r
2605         {\r
2606                 va_list argList;\r
2607                 va_start(argList, szFmt);\r
2608                 AppendFormatV(szFmt, argList);\r
2609                 va_end(argList);\r
2610         }\r
2611 \r
2612         #define MAX_FMT_TRIES           5        // #of times we try \r
2613         #define FMT_BLOCK_SIZE          2048 // # of bytes to increment per try\r
2614         #define BUFSIZE_1ST     256\r
2615         #define BUFSIZE_2ND 512\r
2616         #define STD_BUF_SIZE            1024\r
2617 \r
2618         // an efficient way to add formatted characters to the string.  You may only\r
2619         // add up to STD_BUF_SIZE characters at a time, though\r
2620         void AppendFormatV(const CT* szFmt, va_list argList)\r
2621         {\r
2622                 CT szBuf[STD_BUF_SIZE];\r
2623         #if defined(SS_ANSI) || !defined(_MSC_VER)\r
2624                 int nLen = ssvsprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);\r
2625         #else\r
2626                 int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);\r
2627         #endif\r
2628                 if ( 0 < nLen )\r
2629                         this->append(szBuf, nLen);\r
2630         }\r
2631 \r
2632         // -------------------------------------------------------------------------\r
2633         // FUNCTION:  FormatV\r
2634         //              void FormatV(PCSTR szFormat, va_list, argList);\r
2635         //           \r
2636         // DESCRIPTION:\r
2637         //              This function formats the string with sprintf style format-specs. \r
2638         //              It makes a general guess at required buffer size and then tries\r
2639         //              successively larger buffers until it finds one big enough or a\r
2640         //              threshold (MAX_FMT_TRIES) is exceeded.\r
2641         //\r
2642         // PARAMETERS:\r
2643         //              szFormat - a PCSTR holding the format of the output\r
2644         //              argList - a Microsoft specific va_list for variable argument lists\r
2645         //\r
2646         // RETURN VALUE:\r
2647         // -------------------------------------------------------------------------\r
2648 \r
2649         void FormatV(const CT* szFormat, va_list argList)\r
2650         {\r
2651         #if defined(SS_ANSI) || !defined(_MSC_VER)\r
2652                 int nLen        = sslen(szFormat) + STD_BUF_SIZE;\r
2653                 ssvsprintf(GetBuffer(nLen), nLen-1, szFormat, argList);\r
2654                 ReleaseBuffer();\r
2655 \r
2656         #else\r
2657 \r
2658                 CT* pBuf                        = NULL;\r
2659                 int nChars                      = 1;\r
2660                 int nUsed                       = 0;\r
2661                 size_type nActual       = 0;\r
2662                 int nTry                        = 0;\r
2663 \r
2664                 do\r
2665                 {\r
2666                         // Grow more than linearly (e.g. 512, 1536, 3072, etc)\r
2667 \r
2668                         nChars                  += ((nTry+1) * FMT_BLOCK_SIZE);\r
2669                         pBuf                    = reinterpret_cast<CT*>(_alloca(sizeof(CT)*nChars));\r
2670                         nUsed                   = ssnprintf(pBuf, nChars-1, szFormat, argList);\r
2671 \r
2672                         // Ensure proper NULL termination.\r
2673 \r
2674                         nActual                 = nUsed == -1 ? nChars-1 : SSMIN(nUsed, nChars-1);\r
2675                         pBuf[nActual+1]= '\0';\r
2676 \r
2677 \r
2678                 } while ( nUsed < 0 && nTry++ < MAX_FMT_TRIES );\r
2679 \r
2680                 // assign whatever we managed to format\r
2681 \r
2682                 this->assign(pBuf, nActual);\r
2683 \r
2684         #endif\r
2685         }\r
2686         \r
2687 \r
2688         // -------------------------------------------------------------------------\r
2689         // CString Facade Functions:\r
2690         //\r
2691         // The following methods are intended to allow you to use this class as a\r
2692         // drop-in replacement for CString.\r
2693         // -------------------------------------------------------------------------\r
2694         #ifdef SS_WIN32\r
2695                 BSTR AllocSysString() const\r
2696                 {\r
2697                         ostring os;\r
2698                         ssasn(os, *this);\r
2699                         return ::SysAllocString(os.c_str());\r
2700                 }\r
2701         #endif\r
2702 \r
2703         int Collate(PCMYSTR szThat) const\r
2704         {\r
2705                 return sscoll(this->c_str(), this->length(), szThat, sslen(szThat));\r
2706         }\r
2707 \r
2708         int CollateNoCase(PCMYSTR szThat) const\r
2709         {\r
2710                 return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat));\r
2711         }\r
2712 \r
2713         int Compare(PCMYSTR szThat) const\r
2714         {\r
2715                 return this->compare(szThat);   \r
2716         }\r
2717 \r
2718         int CompareNoCase(PCMYSTR szThat)       const\r
2719         {\r
2720                 return ssicmp(this->c_str(), szThat);\r
2721         }\r
2722 \r
2723         int Delete(int nIdx, int nCount=1)\r
2724         {\r
2725         if ( nIdx < 0 )\r
2726                         nIdx = 0;\r
2727 \r
2728                 if ( nIdx < GetLength() )\r
2729                         this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));\r
2730 \r
2731                 return GetLength();\r
2732         }\r
2733 \r
2734         void Empty()\r
2735         {\r
2736                 this->erase();\r
2737         }\r
2738 \r
2739         int Find(CT ch) const\r
2740         {\r
2741                 MYSIZE nIdx     = this->find_first_of(ch);\r
2742                 return static_cast<int>(MYBASE::npos == nIdx  ? -1 : nIdx);\r
2743         }\r
2744 \r
2745         int Find(PCMYSTR szSub) const\r
2746         {\r
2747                 MYSIZE nIdx     = this->find(szSub);\r
2748                 return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);\r
2749         }\r
2750 \r
2751         int Find(CT ch, int nStart) const\r
2752         {\r
2753                 // CString::Find docs say add 1 to nStart when it's not zero\r
2754                 // CString::Find code doesn't do that however.  We'll stick\r
2755                 // with what the code does\r
2756 \r
2757                 MYSIZE nIdx     = this->find_first_of(ch, static_cast<MYSIZE>(nStart));\r
2758                 return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);\r
2759         }\r
2760 \r
2761         int Find(PCMYSTR szSub, int nStart) const\r
2762         {\r
2763                 // CString::Find docs say add 1 to nStart when it's not zero\r
2764                 // CString::Find code doesn't do that however.  We'll stick\r
2765                 // with what the code does\r
2766 \r
2767                 MYSIZE nIdx     = this->find(szSub, static_cast<MYSIZE>(nStart));\r
2768                 return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);\r
2769         }\r
2770 \r
2771         int FindOneOf(PCMYSTR szCharSet) const\r
2772         {\r
2773                 MYSIZE nIdx = this->find_first_of(szCharSet);\r
2774                 return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);\r
2775         }\r
2776 \r
2777 #ifndef SS_ANSI\r
2778         void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)\r
2779         {\r
2780                 va_list argList;\r
2781                 va_start(argList, szFormat);\r
2782                 PMYSTR szTemp;\r
2783                 if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,\r
2784                                            szFormat, 0, 0,\r
2785                                            reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||\r
2786                          szTemp == 0 )\r
2787                 {\r
2788                         throw std::runtime_error("out of memory");\r
2789                 }\r
2790                 *this = szTemp;\r
2791                 LocalFree(szTemp);\r
2792                 va_end(argList);\r
2793         }\r
2794 \r
2795         void FormatMessage(UINT nFormatId, ...) throw(std::exception)\r
2796         {\r
2797                 MYTYPE sFormat;\r
2798                 VERIFY(sFormat.LoadString(nFormatId) != 0);\r
2799                 va_list argList;\r
2800                 va_start(argList, nFormatId);\r
2801                 PMYSTR szTemp;\r
2802                 if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,\r
2803                                            sFormat, 0, 0,\r
2804                                            reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||\r
2805                         szTemp == 0)\r
2806                 {\r
2807                         throw std::runtime_error("out of memory");\r
2808                 }\r
2809                 *this = szTemp;\r
2810                 LocalFree(szTemp);\r
2811                 va_end(argList);\r
2812         }\r
2813 #endif\r
2814 \r
2815 \r
2816         // -------------------------------------------------------------------------\r
2817         // GetXXXX -- Direct access to character buffer\r
2818         // -------------------------------------------------------------------------\r
2819         CT GetAt(int nIdx) const\r
2820         {\r
2821                 return this->at(static_cast<MYSIZE>(nIdx));\r
2822         }\r
2823 \r
2824         CT* GetBuffer(int nMinLen=-1)\r
2825         {\r
2826                 return GetBuf(nMinLen);\r
2827         }\r
2828 \r
2829         CT* GetBufferSetLength(int nLen)\r
2830         {\r
2831                 return BufferSet(nLen);\r
2832         }\r
2833 \r
2834         // GetLength() -- MFC docs say this is the # of BYTES but\r
2835         // in truth it is the number of CHARACTERs (chars or wchar_ts)\r
2836         int GetLength() const\r
2837         {\r
2838                 return static_cast<int>(this->length());\r
2839         }\r
2840 \r
2841         \r
2842         int Insert(int nIdx, CT ch)\r
2843         {\r
2844                 if ( static_cast<MYSIZE>(nIdx) > this->size() -1 )\r
2845                         this->append(1, ch);\r
2846                 else\r
2847                         this->insert(static_cast<MYSIZE>(nIdx), 1, ch);\r
2848 \r
2849                 return GetLength();\r
2850         }\r
2851         int Insert(int nIdx, PCMYSTR sz)\r
2852         {\r
2853                 if ( nIdx >= this->size() )\r
2854                         this->append(sz, sslen(sz));\r
2855                 else\r
2856                         this->insert(static_cast<MYSIZE>(nIdx), sz);\r
2857 \r
2858                 return GetLength();\r
2859         }\r
2860 \r
2861         bool IsEmpty() const\r
2862         {\r
2863                 return this->empty();\r
2864         }\r
2865 \r
2866         MYTYPE Left(int nCount) const\r
2867         {\r
2868         // Range check the count.\r
2869 \r
2870                 nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));\r
2871                 return this->substr(0, static_cast<MYSIZE>(nCount)); \r
2872         }\r
2873 \r
2874 #ifndef SS_ANSI\r
2875         bool LoadString(UINT nId)\r
2876         {\r
2877                 return this->Load(nId);\r
2878         }\r
2879 #endif\r
2880 \r
2881         void MakeLower()\r
2882         {\r
2883                 ToLower();\r
2884         }\r
2885 \r
2886         void MakeReverse()\r
2887         {\r
2888                 std::reverse(this->begin(), this->end());\r
2889         }\r
2890 \r
2891         void MakeUpper()\r
2892         { \r
2893                 ToUpper();\r
2894         }\r
2895 \r
2896         MYTYPE Mid(int nFirst ) const\r
2897         {\r
2898                 return Mid(nFirst, size()-nFirst);\r
2899         }\r
2900 \r
2901         MYTYPE Mid(int nFirst, int nCount) const\r
2902         {\r
2903                 // CString does range checking here.  Since we're trying to emulate it,\r
2904                 // we must check too.\r
2905 \r
2906                 if ( nFirst < 0 )\r
2907                         nFirst = 0;\r
2908                 if ( nCount < 0 )\r
2909                         nCount = 0;\r
2910 \r
2911                 if ( nFirst + nCount > size() )\r
2912                         nCount = size() - nFirst;\r
2913 \r
2914                 if ( nFirst > size() )\r
2915                         return MYTYPE();\r
2916 \r
2917                 ASSERT(nFirst >= 0);\r
2918                 ASSERT(nFirst + nCount <= size());\r
2919 \r
2920                 return this->substr(static_cast<MYSIZE>(nFirst),\r
2921                                                         static_cast<MYSIZE>(nCount));\r
2922         }\r
2923 \r
2924         void ReleaseBuffer(int nNewLen=-1)\r
2925         {\r
2926                 RelBuf(nNewLen);\r
2927         }\r
2928 \r
2929         int Remove(CT ch)\r
2930         {\r
2931                 MYSIZE nIdx             = 0;\r
2932                 int nRemoved    = 0;\r
2933                 while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos )\r
2934                 {\r
2935                         this->erase(nIdx, 1);\r
2936                         nRemoved++;\r
2937                 }\r
2938                 return nRemoved;\r
2939         }\r
2940 \r
2941         int Replace(CT chOld, CT chNew)\r
2942         {\r
2943                 int nReplaced   = 0;\r
2944                 for ( MYITER iter=this->begin(); iter != this->end(); iter++ )\r
2945                 {\r
2946                         if ( *iter == chOld )\r
2947                         {\r
2948                                 *iter = chNew;\r
2949                                 nReplaced++;\r
2950                         }\r
2951                 }\r
2952                 return nReplaced;\r
2953         }\r
2954 \r
2955         int Replace(PCMYSTR szOld, PCMYSTR szNew)\r
2956         {\r
2957                 int nReplaced           = 0;\r
2958                 MYSIZE nIdx                     = 0;\r
2959                 MYSIZE nOldLen          = sslen(szOld);\r
2960                 if ( 0 == nOldLen )\r
2961                         return 0;\r
2962 \r
2963                 static const CT ch      = CT(0);\r
2964                 MYSIZE nNewLen          = sslen(szNew);\r
2965                 PCMYSTR szRealNew       = szNew == 0 ? &ch : szNew;\r
2966 \r
2967                 while ( (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )\r
2968                 {\r
2969                         replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen, szRealNew);\r
2970                         nReplaced++;\r
2971                         nIdx += nNewLen;\r
2972                 }\r
2973                 return nReplaced;\r
2974         }\r
2975 \r
2976         int ReverseFind(CT ch) const\r
2977         {\r
2978                 MYSIZE nIdx     = this->find_last_of(ch);\r
2979                 return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);\r
2980         }\r
2981 \r
2982         // ReverseFind overload that's not in CString but might be useful\r
2983         int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const\r
2984         {\r
2985                 MYSIZE nIdx     = this->rfind(0 == szFind ? MYTYPE() : szFind, pos);\r
2986                 return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);\r
2987         }\r
2988 \r
2989         MYTYPE Right(int nCount) const\r
2990         {\r
2991         // Range check the count.\r
2992 \r
2993                 nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));\r
2994                 return this->substr(this->size()-static_cast<MYSIZE>(nCount));\r
2995         }\r
2996 \r
2997         void SetAt(int nIndex, CT ch)\r
2998         {\r
2999                 ASSERT(this->size() > static_cast<MYSIZE>(nIndex));\r
3000                 this->at(static_cast<MYSIZE>(nIndex))           = ch;\r
3001         }\r
3002 \r
3003 #ifndef SS_ANSI\r
3004         BSTR SetSysString(BSTR* pbstr) const\r
3005         {\r
3006                 ostring os;\r
3007                 ssasn(os, *this);\r
3008                 if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )\r
3009                         throw std::runtime_error("out of memory");\r
3010 \r
3011                 ASSERT(*pbstr != 0);\r
3012                 return *pbstr;\r
3013         }\r
3014 #endif\r
3015 \r
3016         MYTYPE SpanExcluding(PCMYSTR szCharSet) const\r
3017         {\r
3018         MYSIZE pos = this->find_first_of(szCharSet);\r
3019         return pos == MYBASE::npos ? *this : Left(pos);\r
3020         }\r
3021 \r
3022         MYTYPE SpanIncluding(PCMYSTR szCharSet) const\r
3023         {\r
3024         MYSIZE pos = this->find_first_not_of(szCharSet);\r
3025         return pos == MYBASE::npos ? *this : Left(pos);\r
3026         }\r
3027 \r
3028 #if !defined(UNICODE) && !defined(SS_ANSI)\r
3029 \r
3030         // CString's OemToAnsi and AnsiToOem functions are available only in\r
3031         // Unicode builds.  However since we're a template we also need a\r
3032         // runtime check of CT and a reinterpret_cast to account for the fact\r
3033         // that CStdStringW gets instantiated even in non-Unicode builds.\r
3034 \r
3035         void AnsiToOem()\r
3036         {\r
3037                 if ( sizeof(CT) == sizeof(char) && !empty() )\r
3038                 {\r
3039                         ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()),\r
3040                                                 reinterpret_cast<PSTR>(GetBuf()));\r
3041                 }\r
3042                 else\r
3043                 {\r
3044                         ASSERT(false);\r
3045                 }\r
3046         }\r
3047 \r
3048         void OemToAnsi()\r
3049         {\r
3050                 if ( sizeof(CT) == sizeof(char) && !empty() )\r
3051                 {\r
3052                         ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()),\r
3053                                                 reinterpret_cast<PSTR>(GetBuf()));\r
3054                 }\r
3055                 else\r
3056                 {\r
3057                         ASSERT(false);\r
3058                 }\r
3059         }\r
3060 \r
3061 #endif\r
3062         \r
3063 \r
3064         // -------------------------------------------------------------------------\r
3065         // Trim and its variants\r
3066         // -------------------------------------------------------------------------\r
3067         MYTYPE& Trim()\r
3068         {\r
3069                 return TrimLeft().TrimRight();\r
3070         }\r
3071 \r
3072         MYTYPE& TrimLeft()\r
3073         {\r
3074                 this->erase(this->begin(),\r
3075                         std::find_if(this->begin(), this->end(), NotSpace<CT>()));\r
3076 \r
3077                 return *this;\r
3078         }\r
3079 \r
3080         MYTYPE&  TrimLeft(CT tTrim)\r
3081         {\r
3082                 this->erase(0, this->find_first_not_of(tTrim));\r
3083                 return *this;\r
3084         }\r
3085 \r
3086         MYTYPE&  TrimLeft(PCMYSTR szTrimChars)\r
3087         {\r
3088                 this->erase(0, this->find_first_not_of(szTrimChars));\r
3089                 return *this;\r
3090         }\r
3091 \r
3092         MYTYPE& TrimRight()\r
3093         {\r
3094                 // NOTE:  When comparing reverse_iterators here (MYRITER), I avoid using\r
3095                 // operator!=.  This is because namespace rel_ops also has a template\r
3096                 // operator!= which conflicts with the global operator!= already defined\r
3097                 // for reverse_iterator in the header <utility>.\r
3098                 // Thanks to John James for alerting me to this.\r
3099 \r
3100                 MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>());\r
3101                 if ( !(this->rend() == it) )\r
3102                         this->erase(this->rend() - it);\r
3103 \r
3104                 this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0);\r
3105                 return *this;\r
3106         }\r
3107 \r
3108         MYTYPE&  TrimRight(CT tTrim)\r
3109         {\r
3110                 MYSIZE nIdx     = this->find_last_not_of(tTrim);\r
3111                 this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);\r
3112                 return *this;\r
3113         }\r
3114 \r
3115         MYTYPE&  TrimRight(PCMYSTR szTrimChars)\r
3116         {\r
3117                 MYSIZE nIdx     = this->find_last_not_of(szTrimChars);\r
3118                 this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);\r
3119                 return *this;\r
3120         }\r
3121 \r
3122         void                    FreeExtra()\r
3123         {\r
3124                 MYTYPE mt;\r
3125                 this->swap(mt);\r
3126                 if ( !mt.empty() )\r
3127                         this->assign(mt.c_str(), mt.size());\r
3128         }\r
3129 \r
3130         // I have intentionally not implemented the following CString\r
3131         // functions.   You cannot make them work without taking advantage\r
3132         // of implementation specific behavior.  However if you absolutely\r
3133         // MUST have them, uncomment out these lines for "sort-of-like"\r
3134         // their behavior.  You're on your own.\r
3135 \r
3136 //      CT*                             LockBuffer()    { return GetBuf(); }// won't really lock\r
3137 //      void                    UnlockBuffer(); { }     // why have UnlockBuffer w/o LockBuffer?\r
3138 \r
3139         // Array-indexing operators.  Required because we defined an implicit cast\r
3140         // to operator const CT* (Thanks to Julian Selman for pointing this out)\r
3141         CT& operator[](int nIdx)\r
3142         {\r
3143                 return MYBASE::operator[](static_cast<MYSIZE>(nIdx));\r
3144         }\r
3145 \r
3146         const CT& operator[](int nIdx) const\r
3147         {\r
3148                 return MYBASE::operator[](static_cast<MYSIZE>(nIdx));\r
3149         }\r
3150 \r
3151         CT& operator[](unsigned int nIdx)\r
3152         {\r
3153                 return MYBASE::operator[](static_cast<MYSIZE>(nIdx));\r
3154         }\r
3155 \r
3156         const CT& operator[](unsigned int nIdx) const\r
3157         {\r
3158                 return MYBASE::operator[](static_cast<MYSIZE>(nIdx));\r
3159         }\r
3160 \r
3161 #ifndef SS_NO_IMPLICIT_CAST\r
3162         operator const CT*() const\r
3163         {\r
3164                 return this->c_str();\r
3165         }\r
3166 #endif\r
3167 \r
3168         // IStream related functions.  Useful in IPersistStream implementations\r
3169 \r
3170 #ifdef SS_INC_COMDEF\r
3171 \r
3172         // struct SSSHDR - useful for non Std C++ persistence schemes.\r
3173         typedef struct SSSHDR\r
3174         {\r
3175                 BYTE    byCtrl;\r
3176                 ULONG   nChars;\r
3177         } SSSHDR;       // as in "Standard String Stream Header"\r
3178 \r
3179         #define SSSO_UNICODE    0x01    // the string is a wide string\r
3180         #define SSSO_COMPRESS   0x02    // the string is compressed\r
3181 \r
3182         // -------------------------------------------------------------------------\r
3183         // FUNCTION: StreamSize\r
3184         // REMARKS:\r
3185         //              Returns how many bytes it will take to StreamSave() this CStdString\r
3186         //              object to an IStream.\r
3187         // -------------------------------------------------------------------------\r
3188         ULONG StreamSize() const\r
3189         {\r
3190                 // Control header plus string\r
3191                 ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));\r
3192                 return (this->size() * sizeof(CT)) + sizeof(SSSHDR);\r
3193         }\r
3194 \r
3195         // -------------------------------------------------------------------------\r
3196         // FUNCTION: StreamSave\r
3197         // REMARKS:\r
3198         //              Saves this CStdString object to a COM IStream.\r
3199         // -------------------------------------------------------------------------\r
3200         HRESULT StreamSave(IStream* pStream) const\r
3201         {\r
3202                 ASSERT(size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));\r
3203                 HRESULT hr              = E_FAIL;\r
3204                 ASSERT(pStream != 0);\r
3205                 SSSHDR hdr;\r
3206                 hdr.byCtrl              = sizeof(CT) == 2 ? SSSO_UNICODE : 0;\r
3207                 hdr.nChars              = this->size();\r
3208 \r
3209 \r
3210                 if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )\r
3211                         TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);\r
3212                 else if ( empty() )\r
3213                         ;               // nothing to write\r
3214                 else if ( FAILED(hr=pStream->Write(this->c_str(), this->size()*sizeof(CT), 0)) )\r
3215                         TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);\r
3216 \r
3217                 return hr;\r
3218         }\r
3219 \r
3220 \r
3221         // -------------------------------------------------------------------------\r
3222         // FUNCTION: StreamLoad\r
3223         // REMARKS:\r
3224         //              This method loads the object from an IStream.\r
3225         // -------------------------------------------------------------------------\r
3226         HRESULT StreamLoad(IStream* pStream)\r
3227         {\r
3228                 ASSERT(pStream != 0);\r
3229                 SSSHDR hdr;\r
3230                 HRESULT hr                      = E_FAIL;\r
3231 \r
3232                 if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )\r
3233                 {\r
3234                         TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);\r
3235                 }\r
3236                 else if ( hdr.nChars > 0 )\r
3237                 {\r
3238                         ULONG nRead             = 0;\r
3239                         PMYSTR pMyBuf   = BufferSet(hdr.nChars);\r
3240 \r
3241                         // If our character size matches the character size of the string\r
3242                         // we're trying to read, then we can read it directly into our\r
3243                         // buffer. Otherwise, we have to read into an intermediate buffer\r
3244                         // and convert.\r
3245                         \r
3246                         if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )\r
3247                         {\r
3248                                 ULONG nBytes    = hdr.nChars * sizeof(wchar_t);\r
3249                                 if ( sizeof(CT) == sizeof(wchar_t) )\r
3250                                 {\r
3251                                         if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )\r
3252                                                 TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);\r
3253                                 }\r
3254                                 else\r
3255                                 {       \r
3256                                         PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));\r
3257                                         if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )\r
3258                                                 TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);\r
3259                                         else\r
3260                                                 sscpy(pMyBuf, pBufW, hdr.nChars);\r
3261                                 }\r
3262                         }\r
3263                         else\r
3264                         {\r
3265                                 ULONG nBytes    = hdr.nChars * sizeof(char);\r
3266                                 if ( sizeof(CT) == sizeof(char) )\r
3267                                 {\r
3268                                         if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )\r
3269                                                 TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);\r
3270                                 }\r
3271                                 else\r
3272                                 {\r
3273                                         PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));\r
3274                                         if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )\r
3275                                                 TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);\r
3276                                         else\r
3277                                                 sscpy(pMyBuf, pBufA, hdr.nChars);\r
3278                                 }\r
3279                         }\r
3280                 }\r
3281                 else\r
3282                 {\r
3283                         this->erase();\r
3284                 }\r
3285                 return hr;\r
3286         }\r
3287 #endif // #ifdef SS_INC_COMDEF\r
3288 \r
3289 #ifndef SS_ANSI\r
3290 \r
3291         // SetResourceHandle/GetResourceHandle.  In MFC builds, these map directly\r
3292         // to AfxSetResourceHandle and AfxGetResourceHandle.  In non-MFC builds they\r
3293         // point to a single static HINST so that those who call the member\r
3294         // functions that take resource IDs can provide an alternate HINST of a DLL\r
3295         // to search.  This is not exactly the list of HMODULES that MFC provides\r
3296         // but it's better than nothing.\r
3297 \r
3298 #ifdef _MFC_VER\r
3299         static void SetResourceHandle(HMODULE hNew)\r
3300         {\r
3301                 AfxSetResourceHandle(hNew);\r
3302         }\r
3303         static HMODULE GetResourceHandle()\r
3304         {\r
3305                 return AfxGetResourceHandle();\r
3306         }\r
3307 #else\r
3308         static void SetResourceHandle(HMODULE hNew)\r
3309         {\r
3310                 SSResourceHandle() = hNew;\r
3311         }\r
3312         static HMODULE GetResourceHandle()\r
3313         {\r
3314                 return SSResourceHandle();\r
3315         }\r
3316 #endif\r
3317 \r
3318 \r
3319     template<typename CT2>\r
3320     MYTYPE operator+(const CStdStr<CT2>& s2)\r
3321     {\r
3322             MYTYPE strRet(SSREF(*this));\r
3323         strRet += s2.c_str();\r
3324             return strRet;\r
3325     }\r
3326 \r
3327 \r
3328 #endif\r
3329 };\r
3330 \r
3331 \r
3332 \r
3333 // -----------------------------------------------------------------------------\r
3334 // CStdStr friend addition functions defined as inline\r
3335 // -----------------------------------------------------------------------------\r
3336 template<typename CT>\r
3337 inline\r
3338 CStdStr<CT> operator+(const  CStdStr<CT>& str1, const CStdStr<CT>& str2)\r
3339 {\r
3340         CStdStr<CT> strRet(SSREF(str1));\r
3341         strRet.append(str2);\r
3342         return strRet;\r
3343 }\r
3344 \r
3345 template<typename CT>   \r
3346 inline\r
3347 CStdStr<CT> operator+(const  CStdStr<CT>& str, CT t)\r
3348 {\r
3349         // this particular overload is needed for disabling reference counting\r
3350         // though it's only an issue from line 1 to line 2\r
3351 \r
3352         CStdStr<CT> strRet(SSREF(str)); // 1\r
3353         strRet.append(1, t);                            // 2\r
3354         return strRet;\r
3355 }\r
3356 \r
3357 template<typename CT>\r
3358 inline\r
3359 CStdStr<CT> operator+(const  CStdStr<CT>& str, PCSTR pA)\r
3360 {\r
3361         return CStdStr<CT>(str) + CStdStr<CT>(pA);\r
3362 }\r
3363 \r
3364 template<typename CT>\r
3365 inline\r
3366 CStdStr<CT> operator+(PCSTR pA, const CStdStr<CT>& str)\r
3367 {\r
3368         CStdStr<CT> strRet(pA);\r
3369         strRet.append(str);\r
3370         return strRet;\r
3371 }\r
3372 \r
3373 template<typename CT>\r
3374 inline\r
3375 CStdStr<CT> operator+(const CStdStr<CT>& str, PCWSTR pW)\r
3376\r
3377         return CStdStr<CT>(SSREF(str)) + CStdStr<CT>(pW);\r
3378 }\r
3379 \r
3380 template<typename CT>\r
3381 inline\r
3382 CStdStr<CT> operator+(PCWSTR pW, const CStdStr<CT>& str)\r
3383 {\r
3384         CStdStr<CT> strRet(pW);\r
3385         strRet.append(str);\r
3386         return strRet;\r
3387 }\r
3388 \r
3389 #ifdef SS_INC_COMDEF\r
3390         template<typename CT>\r
3391         inline\r
3392         CStdStr<CT> operator+(const _bstr_t& bstr, const CStdStr<CT>& str)\r
3393         {\r
3394                 return static_cast<const CT*>(bstr) + str;\r
3395         }\r
3396 \r
3397         template<typename CT>\r
3398         inline\r
3399         CStdStr<CT> operator+(const CStdStr<CT>& str, const _bstr_t& bstr)\r
3400         {\r
3401                 return str + static_cast<const CT*>(bstr);\r
3402         }\r
3403 #endif\r
3404 \r
3405 // -----------------------------------------------------------------------------\r
3406 // HOW TO EXPORT CSTDSTRING FROM A DLL\r
3407 //\r
3408 // If you want to export CStdStringA and CStdStringW from a DLL, then all you\r
3409 // need to\r
3410 //              1.      make sure that all components link to the same DLL version\r
3411 //                      of the CRT (not the static one).\r
3412 //              2.      Uncomment the 3 lines of code below\r
3413 //              3.      #define 2 macros per the instructions in MS KnowledgeBase\r
3414 //                      article Q168958.  The macros are:\r
3415 //\r
3416 //              MACRO           DEFINTION WHEN EXPORTING                DEFINITION WHEN IMPORTING\r
3417 //              -----           ------------------------                -------------------------\r
3418 //              SSDLLEXP        (nothing, just #define it)              extern\r
3419 //              SSDLLSPEC       __declspec(dllexport)                   __declspec(dllimport)\r
3420 //\r
3421 //              Note that these macros must be available to ALL clients who want to \r
3422 //              link to the DLL and use the class.  If they \r
3423 // -----------------------------------------------------------------------------\r
3424 //#pragma warning(disable:4231) // non-standard extension ("extern template")\r
3425 //      SSDLLEXP template class SSDLLSPEC CStdStr<char>;\r
3426 //      SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;\r
3427 \r
3428 // =============================================================================\r
3429 //                                              END OF CStdStr INLINE FUNCTION DEFINITIONS\r
3430 // =============================================================================\r
3431 \r
3432 //      Now typedef our class names based upon this humongous template\r
3433 \r
3434 typedef CStdStr<char>           CStdStringA;    // a better std::string\r
3435 typedef CStdStr<wchar_t>        CStdStringW;    // a better std::wstring\r
3436 typedef CStdStr<OLECHAR>        CStdStringO;    // almost always CStdStringW\r
3437 \r
3438 \r
3439 // New-style format function is a template\r
3440 \r
3441 #ifdef SS_SAFE_FORMAT\r
3442 \r
3443 template<>\r
3444 struct FmtArg<CStdStringA>\r
3445 {\r
3446     explicit FmtArg(const CStdStringA& arg) : a_(arg) {}\r
3447     PCSTR Val() const { return a_.c_str(); }\r
3448     const CStdStringA& a_;\r
3449 private:\r
3450     FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; }\r
3451 };\r
3452 template<>\r
3453 struct FmtArg<CStdStringW>\r
3454 {\r
3455     explicit FmtArg(const CStdStringW& arg) : a_(arg) {}\r
3456     PCWSTR Val() const { return a_.c_str(); }\r
3457     const CStdStringW& a_;\r
3458 private:\r
3459     FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; }\r
3460 };\r
3461 \r
3462 template<>\r
3463 struct FmtArg<std::string>\r
3464 {\r
3465     explicit FmtArg(const std::string& arg) : a_(arg) {}\r
3466     PCSTR Val() const { return a_.c_str(); }\r
3467     const std::string& a_;\r
3468 private:\r
3469     FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; }\r
3470 };\r
3471 template<>\r
3472 struct FmtArg<std::wstring>\r
3473 {\r
3474     explicit FmtArg(const std::wstring& arg) : a_(arg) {}\r
3475     PCWSTR Val() const { return a_.c_str(); }\r
3476     const std::wstring& a_;\r
3477 private:\r
3478     FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;}\r
3479 };\r
3480 #endif // #ifdef SS_SAFEFORMAT\r
3481 \r
3482 #ifndef SS_ANSI\r
3483         // SSResourceHandle: our MFC-like resource handle\r
3484         inline HMODULE& SSResourceHandle()\r
3485         {\r
3486                 static HMODULE hModuleSS        = GetModuleHandle(0);\r
3487                 return hModuleSS;\r
3488         }\r
3489 #endif\r
3490 \r
3491 \r
3492 \r
3493 \r
3494 // In MFC builds, define some global serialization operators\r
3495 // Special operators that allow us to serialize CStdStrings to CArchives.\r
3496 // Note that we use an intermediate CString object in order to ensure that\r
3497 // we use the exact same format.\r
3498 \r
3499 #ifdef _MFC_VER\r
3500         inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)\r
3501         {\r
3502                 CString strTemp = strA;\r
3503                 return ar << strTemp;\r
3504         }\r
3505         inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)\r
3506         {\r
3507                 CString strTemp = strW;\r
3508                 return ar << strTemp;\r
3509         }\r
3510 \r
3511         inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)\r
3512         {\r
3513                 CString strTemp;\r
3514                 ar >> strTemp;\r
3515                 strA = strTemp;\r
3516                 return ar;\r
3517         }\r
3518         inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)\r
3519         {\r
3520                 CString strTemp;\r
3521                 ar >> strTemp;\r
3522                 strW = strTemp;\r
3523                 return ar;\r
3524         }\r
3525 #endif  // #ifdef _MFC_VER -- (i.e. is this MFC?)\r
3526 \r
3527 \r
3528 \r
3529 // -----------------------------------------------------------------------------\r
3530 // GLOBAL FUNCTION:  WUFormat\r
3531 //              CStdStringA WUFormat(UINT nId, ...);\r
3532 //              CStdStringA WUFormat(PCSTR szFormat, ...);\r
3533 //\r
3534 // REMARKS:\r
3535 //              This function allows the caller for format and return a CStdStringA\r
3536 //              object with a single line of code.\r
3537 // -----------------------------------------------------------------------------\r
3538 #ifdef SS_ANSI\r
3539 #else\r
3540 \r
3541         inline CStdStringA WUFormatA(UINT nId, ...)\r
3542         {\r
3543                 va_list argList;\r
3544                 va_start(argList, nId);\r
3545 \r
3546                 CStdStringA strFmt;\r
3547                 CStdStringA strOut;\r
3548                 if ( strFmt.Load(nId) )\r
3549                         strOut.FormatV(strFmt, argList);\r
3550 \r
3551                 va_end(argList);\r
3552                 return strOut;\r
3553         }\r
3554         inline CStdStringA WUFormatA(PCSTR szFormat, ...)\r
3555         {\r
3556                 va_list argList;\r
3557                 va_start(argList, szFormat);\r
3558                 CStdStringA strOut;\r
3559                 strOut.FormatV(szFormat, argList);\r
3560                 va_end(argList);\r
3561                 return strOut;\r
3562         }\r
3563 \r
3564         inline CStdStringW WUFormatW(UINT nId, ...)\r
3565         {\r
3566                 va_list argList;\r
3567                 va_start(argList, nId);\r
3568 \r
3569                 CStdStringW strFmt;\r
3570                 CStdStringW strOut;\r
3571                 if ( strFmt.Load(nId) )\r
3572                         strOut.FormatV(strFmt, argList);\r
3573 \r
3574                 va_end(argList);\r
3575                 return strOut;\r
3576         }\r
3577         inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)\r
3578         {\r
3579                 va_list argList;\r
3580                 va_start(argList, szwFormat);\r
3581                 CStdStringW strOut;\r
3582                 strOut.FormatV(szwFormat, argList);\r
3583                 va_end(argList);\r
3584                 return strOut;\r
3585         }\r
3586 #endif // #ifdef SS_ANSI\r
3587 \r
3588 #ifdef SS_WIN32\r
3589         // -------------------------------------------------------------------------\r
3590         // FUNCTION: WUSysMessage\r
3591         //       CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);\r
3592         //       CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);\r
3593         //           \r
3594         // DESCRIPTION:\r
3595         //       This function simplifies the process of obtaining a string equivalent\r
3596         //       of a system error code returned from GetLastError().  You simply\r
3597         //       supply the value returned by GetLastError() to this function and the\r
3598         //       corresponding system string is returned in the form of a CStdStringA.\r
3599         //\r
3600         // PARAMETERS: \r
3601         //       dwError - a DWORD value representing the error code to be translated\r
3602         //       dwLangId - the language id to use.  defaults to english.\r
3603         //\r
3604         // RETURN VALUE: \r
3605         //       a CStdStringA equivalent of the error code.  Currently, this function\r
3606         //       only returns either English of the system default language strings.  \r
3607         // -------------------------------------------------------------------------\r
3608         #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)\r
3609         inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)\r
3610         {\r
3611                 CHAR szBuf[512];\r
3612 \r
3613                 if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,\r
3614                                                                    dwLangId, szBuf, 511, NULL) )\r
3615                         return WUFormatA("%s (0x%X)", szBuf, dwError);\r
3616                 else\r
3617                         return WUFormatA("Unknown error (0x%X)", dwError);\r
3618         }\r
3619         inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)\r
3620         {\r
3621                 WCHAR szBuf[512];\r
3622 \r
3623                 if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,\r
3624                                                                    dwLangId, szBuf, 511, NULL) )\r
3625                         return WUFormatW(L"%s (0x%X)", szBuf, dwError);\r
3626                 else\r
3627                         return WUFormatW(L"Unknown error (0x%X)", dwError);\r
3628         }\r
3629 #endif\r
3630 \r
3631 // Define TCHAR based friendly names for some of these functions\r
3632 \r
3633 #ifdef UNICODE\r
3634         #define CStdString                              CStdStringW\r
3635         #define WUSysMessage                    WUSysMessageW\r
3636         #define WUFormat                                WUFormatW\r
3637 #else\r
3638         #define CStdString                              CStdStringA\r
3639         #define WUSysMessage                    WUSysMessageA\r
3640         #define WUFormat                                WUFormatA\r
3641 #endif\r
3642 \r
3643 // ...and some shorter names for the space-efficient\r
3644 \r
3645 #define WUSysMsg                                        WUSysMessage\r
3646 #define WUSysMsgA                                       WUSysMessageA\r
3647 #define WUSysMsgW                                       WUSysMessageW\r
3648 #define WUFmtA                                          WUFormatA\r
3649 #define WUFmtW                                          WUFormatW\r
3650 #define WUFmt                                           WUFormat\r
3651 #define WULastErrMsg()                          WUSysMessage(::GetLastError())\r
3652 #define WULastErrMsgA()                         WUSysMessageA(::GetLastError())\r
3653 #define WULastErrMsgW()                         WUSysMessageW(::GetLastError())\r
3654 \r
3655 \r
3656 // -----------------------------------------------------------------------------\r
3657 // FUNCTIONAL COMPARATORS:\r
3658 // REMARKS:\r
3659 //              These structs are derived from the std::binary_function template.  They\r
3660 //              give us functional classes (which may be used in Standard C++ Library\r
3661 //              collections and algorithms) that perform case-insensitive comparisons of\r
3662 //              CStdString objects.  This is useful for maps in which the key may be the\r
3663 //               proper string but in the wrong case.\r
3664 // -----------------------------------------------------------------------------\r
3665 #define StdStringLessNoCaseW            SSLNCW  // avoid VC compiler warning 4786\r
3666 #define StdStringEqualsNoCaseW          SSENCW          \r
3667 #define StdStringLessNoCaseA            SSLNCA          \r
3668 #define StdStringEqualsNoCaseA          SSENCA          \r
3669 \r
3670 #ifdef UNICODE\r
3671         #define StdStringLessNoCase             SSLNCW          \r
3672         #define StdStringEqualsNoCase   SSENCW          \r
3673 #else\r
3674         #define StdStringLessNoCase             SSLNCA          \r
3675         #define StdStringEqualsNoCase   SSENCA          \r
3676 #endif\r
3677 \r
3678 struct StdStringLessNoCaseW\r
3679         : std::binary_function<CStdStringW, CStdStringW, bool>\r
3680 {\r
3681         inline\r
3682         bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const\r
3683         { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }\r
3684 };\r
3685 struct StdStringEqualsNoCaseW\r
3686         : std::binary_function<CStdStringW, CStdStringW, bool>\r
3687 {\r
3688         inline\r
3689         bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const\r
3690         { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }\r
3691 };\r
3692 struct StdStringLessNoCaseA\r
3693         : std::binary_function<CStdStringA, CStdStringA, bool>\r
3694 {\r
3695         inline\r
3696         bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const\r
3697         { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }\r
3698 };\r
3699 struct StdStringEqualsNoCaseA\r
3700         : std::binary_function<CStdStringA, CStdStringA, bool>\r
3701 {\r
3702         inline\r
3703         bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const\r
3704         { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }\r
3705 };\r
3706 \r
3707 // If we had to define our own version of TRACE above, get rid of it now\r
3708 \r
3709 #ifdef TRACE_DEFINED_HERE\r
3710         #undef TRACE\r
3711         #undef TRACE_DEFINED_HERE\r
3712 #endif\r
3713 \r
3714 \r
3715 // These std::swap specializations come courtesy of Mike Crusader. \r
3716 \r
3717 //namespace std\r
3718 //{\r
3719 //      inline void swap(CStdStringA& s1, CStdStringA& s2) throw()\r
3720 //      {\r
3721 //              s1.swap(s2);\r
3722 //      }\r
3723 //      template<>\r
3724 //      inline void swap(CStdStringW& s1, CStdStringW& s2) throw()\r
3725 //      {\r
3726 //              s1.swap(s2);\r
3727 //      }\r
3728 //}\r
3729 \r
3730 // Turn back on any Borland warnings we turned off.\r
3731 \r
3732 #ifdef __BORLANDC__\r
3733     #pragma option pop  // Turn back on inline function warnings\r
3734 //      #pragma warn +inl   // Turn back on inline function warnings\r
3735 #endif\r
3736 \r
3737 #endif  // #ifndef STDSTRING_H