/* StdString - std::string convenience wrapper. */ // ============================================================================= // FILE: StdString.h // AUTHOR: Joe O'Leary (with outside help noted in comments) // REMARKS: // This header file declares the CStdStr template. This template derives // the Standard C++ Library basic_string<> template and add to it the // the following conveniences: // - The full MFC RString set of functions (including implicit cast) // - writing to/reading from COM IStream interfaces // - Functional objects for use in STL algorithms // // From this template, we intstantiate two classes: CStdStringA and // CStdStringW. The name "CStdString" is just a #define of one of these, // based upone the _UNICODE macro setting // // This header also declares our own version of the MFC/ATL UNICODE-MBCS // conversion macros. Our version looks exactly like the Microsoft's to // facilitate portability. // // NOTE: // If you you use this in an MFC or ATL build, you should include either // afx.h or atlbase.h first, as appropriate. // // PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS: // // Several people have helped me iron out problems and othewise improve // this class. OK, this is a long list but in my own defense, this code // has undergone two major rewrites. Many of the improvements became // necessary after I rewrote the code as a template. Others helped me // improve the RString facade. // // Anyway, these people are (in chronological order): // // - Pete the Plumber (???) // - Julian Selman // - Chris (of Melbsys) // - Dave Plummer // - John C Sipos // - Chris Sells // - Nigel Nunn // - Fan Xia // - Matthew Williams // - Carl Engman // - Mark Zeren // - Craig Watson // - Rich Zuris // - Karim Ratib // - Chris Conti // - Baptiste Lepilleur // - Greg Pickles // - Jim Cline // - Jeff Kohn // - Todd Heckel // - Ullrich Pollähne // - Joe Vitaterna // - Joe Woodbury // - Aaron (no last name) // - Joldakowski (???) // - Scott Hathaway // - Eric Nitzche // - Pablo Presedo // - Farrokh Nejadlotfi // - Jason Mills // - Igor Kholodov // - Mike Crusader // - John James // - Wang Haifeng // - Tim Dowty // - Arnt Witteveen // Turn off browser references // Turn off unavoidable compiler warnings #if defined(_MSC_VER) && (_MSC_VER > 1100) #pragma component(browser, off, references, "CStdString") #pragma warning (push) #pragma warning (disable : 4290) // C++ Exception Specification ignored #pragma warning (disable : 4127) // Conditional expression is constant #pragma warning (disable : 4097) // typedef name used as synonym for class name #pragma warning (disable : 4512) // assignment operator could not be generated #endif #ifndef STDSTRING_H #define STDSTRING_H // If they want us to use only standard C++ stuff (no Win32 stuff) typedef const char* PCSTR; typedef char* PSTR; // Standard headers needed #include // basic_string #include // for_each, etc. #include // for StdStringLessNoCase, et al #if defined(WIN32) #include // _alloca #endif #include #include #include #include #include // a very shorthand way of applying the fix for KB problem Q172398 // (basic_string assignment bug) #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 ) #define HAVE_ASSIGN_FIX #define Q172398(x) (x).erase() #else #define Q172398(x) #endif /* In RageUtil: */ void MakeUpper( char *p, size_t iLen ); void MakeLower( char *p, size_t iLen ); void MakeUpper( wchar_t *p, size_t iLen ); void MakeLower( wchar_t *p, size_t iLen ); // ============================================================================= // INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES // // Usually for generic text mapping, we rely on preprocessor macro definitions // to map to string functions. However the CStdStr<> template cannot use // macro-based generic text mappings because its character types do not get // resolved until template processing which comes AFTER macro processing. In // other words, UNICODE is of little help to us in the CStdStr template // // Therefore, to keep the CStdStr declaration simple, we have these inline // functions. The template calls them often. Since they are inline (and NOT // exported when this is built as a DLL), they will probably be resolved away // to nothing. // // Without these functions, the CStdStr<> template would probably have to broken // out into two, almost identical classes. Either that or it would be a huge, // convoluted mess, with tons of "if" statements all over the place checking the // size of template parameter CT. // // In several cases, you will see two versions of each function. One version is // the more portable, standard way of doing things, while the other is the // non-standard, but often significantly faster Visual C++ way. // ============================================================================= namespace StdString { // ----------------------------------------------------------------------------- // sstolower/sstoupper -- convert characters to upper/lower case // ----------------------------------------------------------------------------- //inline char sstoupper(char ch) { return (char)::toupper(ch); } //inline char sstolower(char ch) { return (char)::tolower(ch); } /* Our strings are UTF-8; instead of having to play around with locales, * let's just manually toupper ASCII only. If we really want to play with * Unicode cases, we can do it ourself in RageUtil. */ inline char sstoupper(char ch) { return (ch >= 'a' && ch <= 'z')? char(ch + 'A' - 'a'): ch; } inline char sstolower(char ch) { return (ch >= 'A' && ch <= 'Z')? char(ch + 'a' - 'A'): ch; } // ----------------------------------------------------------------------------- // ssasn: assignment functions -- assign "sSrc" to "sDst" // ----------------------------------------------------------------------------- typedef std::string::size_type SS_SIZETYPE; // just for shorthand, really typedef std::string::pointer SS_PTRTYPE; inline void ssasn(std::string& sDst, const std::string& sSrc) { if ( sDst.c_str() != sSrc.c_str() ) { sDst.erase(); sDst.assign(sSrc); } } inline void ssasn(std::string& sDst, PCSTR pA) { #if defined(HAVE_ASSIGN_FIX) // If pA actually points to part of sDst, we must NOT erase(), but // rather take a substring if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() ) { sDst =sDst.substr(static_cast(pA-sDst.c_str())); } // Otherwise (most cases) apply the assignment bug fix, if applicable // and do the assignment else { Q172398(sDst); sDst.assign(pA); } else #else sDst.assign(pA); #endif } inline void ssasn(std::string& sDst, const int nNull) { sDst.erase(); } #undef StrSizeType // ----------------------------------------------------------------------------- // ssadd: string object concatenation -- add second argument to first // ----------------------------------------------------------------------------- inline void ssadd(std::string& sDst, const std::string& sSrc) { sDst += sSrc; } inline void ssadd(std::string& sDst, PCSTR pA) { // If the string being added is our internal string or a part of our // internal string, then we must NOT do any reallocation without // first copying that string to another object (since we're using a // direct pointer) if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length()) { if ( sDst.capacity() <= sDst.size()+strlen(pA) ) sDst.append(std::string(pA)); else sDst.append(pA); } else { sDst.append(pA); } } // ----------------------------------------------------------------------------- // ssicmp: comparison (case insensitive ) // ----------------------------------------------------------------------------- template inline int ssicmp(const CT* pA1, const CT* pA2) { CT f; CT l; do { f = sstolower(*(pA1++)); l = sstolower(*(pA2++)); } while ( (f) && (f == l) ); return (int)(f - l); } // ----------------------------------------------------------------------------- // ssupr/sslwr: Uppercase/Lowercase conversion functions // ----------------------------------------------------------------------------- #if 0 template inline void sslwr(CT* pT, size_t nLen) { for ( CT* p = pT; static_cast(p - pT) < nLen; ++p) *p = (CT)sstolower(*p); } template inline void ssupr(CT* pT, size_t nLen) { for ( CT* p = pT; static_cast(p - pT) < nLen; ++p) *p = (CT)sstoupper(*p); } #endif inline void sslwr(char *pT, size_t nLen) { MakeLower( pT, nLen ); } inline void ssupr(char *pT, size_t nLen) { MakeUpper( pT, nLen ); } inline void sslwr(wchar_t *pT, size_t nLen) { MakeLower( pT, nLen ); } inline void ssupr(wchar_t *pT, size_t nLen) { MakeUpper( pT, nLen ); } #if defined(WIN32) #define vsnprintf _vsnprintf #endif // Now we can define the template (finally!) // ============================================================================= // TEMPLATE: CStdStr // template class CStdStr : public std::basic_string // // REMARKS: // This template derives from basic_string and adds some MFC RString- // like functionality // // Basically, this is my attempt to make Standard C++ library strings as // easy to use as the MFC RString class. // // Note that although this is a template, it makes the assumption that the // template argument (CT, the character type) is either char or wchar_t. // ============================================================================= //#define CStdStr _SS // avoid compiler warning 4786 template class CStdStr; template inline CStdStr operator+(const CStdStr& str1, const CStdStr& str2) { CStdStr strRet(str1); strRet.append(str2); return strRet; } template inline CStdStr operator+(const CStdStr& str, CT t) { // this particular overload is needed for disabling reference counting // though it's only an issue from line 1 to line 2 CStdStr strRet(str); // 1 strRet.append(1, t); // 2 return strRet; } template inline CStdStr operator+(const CStdStr& str, PCSTR pA) { return CStdStr(str) + CStdStr(pA); } template inline CStdStr operator+(PCSTR pA, const CStdStr& str) { CStdStr strRet(pA); strRet.append(str); return strRet; } template class CStdStr : public std::basic_string { // Typedefs for shorter names. Using these names also appears to help // us avoid some ambiguities that otherwise arise on some platforms typedef typename std::basic_string MYBASE; // my base class typedef CStdStr MYTYPE; // myself typedef typename MYBASE::const_pointer PCMYSTR; // PCSTR typedef typename MYBASE::pointer PMYSTR; // PSTR typedef typename MYBASE::iterator MYITER; // my iterator type typedef typename MYBASE::const_iterator MYCITER; // you get the idea... typedef typename MYBASE::reverse_iterator MYRITER; typedef typename MYBASE::size_type MYSIZE; typedef typename MYBASE::value_type MYVAL; typedef typename MYBASE::allocator_type MYALLOC; typedef typename MYBASE::traits_type MYTRAITS; public: // CStdStr inline constructors CStdStr() { } CStdStr(const MYTYPE& str) : MYBASE(str) { } CStdStr(const std::string& str): MYBASE(str) { } CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n) { } CStdStr(PCSTR pA) { *this = pA; } CStdStr(MYCITER first, MYCITER last) : MYBASE(first, last) { } CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC()) : MYBASE(nSize, ch, al) { } // CStdStr inline assignment operators -- the ssasn function now takes care // of fixing the MSVC assignment bug (see knowledge base article Q172398). MYTYPE& operator=(const MYTYPE& str) { ssasn(*this, str); return *this; } MYTYPE& operator=(const std::string& str) { ssasn(*this, str); return *this; } MYTYPE& operator=(PCSTR pA) { ssasn(*this, pA); return *this; } MYTYPE& operator=(CT t) { Q172398(*this); this->assign(1, t); return *this; } // Overloads also needed to fix the MSVC assignment bug (KB: Q172398) // *** Thanks to Pete The Plumber for catching this one *** // They also are compiled if you have explicitly turned off refcounting #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) MYTYPE& assign(const MYTYPE& str) { ssasn(*this, str); return *this; } MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars) { // This overload of basic_string::assign is supposed to assign up to // or the NULL terminator, whichever comes first. Since we // are about to call a less forgiving overload (in which // must be a valid length), we must adjust the length here to a safe // value. Thanks to Ullrich Pollähne for catching this bug nChars = min(nChars, str.length() - nStart); // Watch out for assignment to self if ( this == &str ) { MYTYPE strTemp(str.c_str()+nStart, nChars); MYBASE::assign(strTemp); } else { Q172398(*this); MYBASE::assign(str.c_str()+nStart, nChars); } return *this; } MYTYPE& assign(const MYBASE& str) { ssasn(*this, str); return *this; } MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars) { // This overload of basic_string::assign is supposed to assign up to // or the NULL terminator, whichever comes first. Since we // are about to call a less forgiving overload (in which // must be a valid length), we must adjust the length here to a safe // value. Thanks to Ullrich Pollähne for catching this bug nChars = min(nChars, str.length() - nStart); // Watch out for assignment to self if ( this == &str ) // watch out for assignment to self { MYTYPE strTemp(str.c_str() + nStart, nChars); MYBASE::assign(strTemp); } else { Q172398(*this); MYBASE::assign(str.c_str()+nStart, nChars); } return *this; } MYTYPE& assign(const CT* pC, MYSIZE nChars) { // Q172398 only fix -- erase before assigning, but not if we're // assigning from our own buffer #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 ) if ( !this->empty() && ( pC < this->data() || pC > this->data() + this->capacity() ) ) { this->erase(); } #endif MYBASE::assign(pC, nChars); return *this; } MYTYPE& assign(MYSIZE nChars, MYVAL val) { Q172398(*this); MYBASE::assign(nChars, val); return *this; } MYTYPE& assign(const CT* pT) { return this->assign(pT, MYBASE::traits_type::length(pT)); } MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast) { #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 ) // Q172398 fix. don't call erase() if we're assigning from ourself if ( iterFirst < this->begin() || iterFirst > this->begin() + this->size() ) this->erase() #endif this->replace(this->begin(), this->end(), iterFirst, iterLast); return *this; } #endif /* VC6 string is missing clear(). */ #if defined(_MSC_VER) && ( _MSC_VER < 1300 ) /* VC6, not VC7 */ void clear() { this->erase(); } #endif // ------------------------------------------------------------------------- // CStdStr inline concatenation. // ------------------------------------------------------------------------- MYTYPE& operator+=(const MYTYPE& str) { ssadd(*this, str); return *this; } MYTYPE& operator+=(const std::string& str) { ssadd(*this, str); return *this; } MYTYPE& operator+=(PCSTR pA) { ssadd(*this, pA); return *this; } MYTYPE& operator+=(CT t) { this->append(1, t); return *this; } // addition operators -- global friend functions. #if defined(_MSC_VER) && _MSC_VER < 1300 /* VC6, not VC7 */ /* work around another stupid vc6 bug */ #define EMP_TEMP #else #define EMP_TEMP <> #endif friend MYTYPE operator+ EMP_TEMP(const MYTYPE& str1, const MYTYPE& str2); friend MYTYPE operator+ EMP_TEMP(const MYTYPE& str, CT t); friend MYTYPE operator+ EMP_TEMP(const MYTYPE& str, PCSTR sz); friend MYTYPE operator+ EMP_TEMP(PCSTR pA, const MYTYPE& str); // ------------------------------------------------------------------------- // Case changing functions // ------------------------------------------------------------------------- MYTYPE& MakeUpper() { if ( !this->empty() ) ssupr(GetBuffer(), this->size()); return *this; } MYTYPE& MakeLower() { if ( !this->empty() ) sslwr(GetBuffer(), this->size()); return *this; } // ------------------------------------------------------------------------- // CStdStr -- Direct access to character buffer. In the MS' implementation, // the at() function that we use here also calls _Freeze() providing us some // protection from multithreading problems associated with ref-counting. // ------------------------------------------------------------------------- CT* GetBuffer(int nMinLen=-1) { if ( static_cast(this->size()) < nMinLen ) this->resize(static_cast(nMinLen)); return this->empty() ? const_cast(this->data()) : &(this->at(0)); } void ReleaseBuffer(int nNewLen=-1) { this->resize(static_cast(nNewLen > -1 ? nNewLen : MYTRAITS::length(this->c_str()))); } // ------------------------------------------------------------------------- // RString Facade Functions: // // The following methods are intended to allow you to use this class as a // drop-in replacement for CString. // ------------------------------------------------------------------------- int CompareNoCase(PCMYSTR szThat) const { return ssicmp(this->c_str(), szThat); } bool EqualsNoCase(PCMYSTR szThat) const { return CompareNoCase(szThat) == 0; } MYTYPE Left(int nCount) const { // Range check the count. nCount = max(0, min(nCount, static_cast(this->size()))); return this->substr(0, static_cast(nCount)); } int Replace(CT chOld, CT chNew) { int nReplaced = 0; for ( MYITER iter=this->begin(); iter != this->end(); iter++ ) { if ( *iter == chOld ) { *iter = chNew; nReplaced++; } } return nReplaced; } int Replace(PCMYSTR szOld, PCMYSTR szNew) { int nReplaced = 0; MYSIZE nIdx = 0; MYSIZE nOldLen = MYTRAITS::length(szOld); if ( 0 == nOldLen ) return 0; static const CT ch = CT(0); MYSIZE nNewLen = MYTRAITS::length(szNew); PCMYSTR szRealNew = szNew == 0 ? &ch : szNew; while ( (nIdx=this->find(szOld, nIdx)) != MYBASE::npos ) { MYBASE::replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen, szRealNew); nReplaced++; nIdx += nNewLen; } return nReplaced; } MYTYPE Right(int nCount) const { // Range check the count. nCount = max(0, min(nCount, static_cast(this->size()))); return this->substr(this->size()-static_cast(nCount)); } // Array-indexing operators. Required because we defined an implicit cast // to operator const CT* (Thanks to Julian Selman for pointing this out) CT& operator[](int nIdx) { return MYBASE::operator[](static_cast(nIdx)); } const CT& operator[](int nIdx) const { return MYBASE::operator[](static_cast(nIdx)); } CT& operator[](unsigned int nIdx) { return MYBASE::operator[](static_cast(nIdx)); } const CT& operator[](unsigned int nIdx) const { return MYBASE::operator[](static_cast(nIdx)); } CT& operator[](long unsigned int nIdx){ return MYBASE::operator[](static_cast(nIdx)); } const CT& operator[](long unsigned int nIdx) const { return MYBASE::operator[](static_cast(nIdx)); } #ifndef SS_NO_IMPLICIT_CASTS operator const CT*() const { return this->c_str(); } #endif }; // ============================================================================= // END OF CStdStr INLINE FUNCTION DEFINITIONS // ============================================================================= // Now typedef our class names based upon this humongous template typedef CStdStr CStdStringA; // a better std::string #define CStdString CStdStringA // ----------------------------------------------------------------------------- // FUNCTIONAL COMPARATORS: // REMARKS: // These structs are derived from the std::binary_function template. They // give us functional classes (which may be used in Standard C++ Library // collections and algorithms) that perform case-insensitive comparisons of // CStdString objects. This is useful for maps in which the key may be the // proper string but in the wrong case. // ----------------------------------------------------------------------------- #define StdStringLessNoCase SSLNCA #define StdStringEqualsNoCase SSENCA struct StdStringLessNoCase : std::binary_function { inline bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; } }; struct StdStringEqualsNoCase : std::binary_function { inline bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; } }; // These std::swap specializations come courtesy of Mike Crusader. //namespace std //{ // inline void swap(CStdStringA& s1, CStdStringA& s2) throw() // { // s1.swap(s2); // } //} } // namespace StdString #if defined(_MSC_VER) && (_MSC_VER > 1100) #pragma warning (pop) #endif #endif // #ifndef STDSTRING_H /* * COPYRIGHT: * 1999 Joseph M. O'Leary. This code is free. Use it anywhere you want. * Rewrite it, restructure it, whatever. Please don't blame me if it makes * your $30 billion dollar satellite explode in orbit. If you redistribute * it in any form, I'd appreciate it if you would leave this notice here. * * If you find any bugs, please let me know: * * jmoleary@earthlink.net * http://home.earthlink.net/~jmoleary */