Universal STL-Based Template String Class

Environment: VC++ 6sp5, VC++ 7.1 (.NET 2003)


Shortcomings of Extant String Classes

There are two reasons that urged me to create one more string class, in spite of the fact that so many of them already exist. Let's consider the following scenarios:

  1. TCHAR-based application extensively using COM, the functions/interface methods of which are WCHAR-based.
  2. TCHAR-based application working with network protocols, which normally make use of ASCII.
  3. Passing strings across module borders (between an executable and a DLL or between DLLs). In this situation, strings are required to use some global memory allocator (such as GlobalAlloc, CoTaskMemAlloc, or VirtualAlloc).
  4. Necessity to have strings that are compared case-insensitively by default. Sometimes, it is desirable as well to compare case-sensitive strings in a case-insensitive manner and vice versa.
  5. Any combination of the previous scenarios or any situation when different kinds of strings must be used jointly.

MFC's CString in VC++ 6.0 is hard-coded as a TCHAR-based class, has no provisions to change its own allocator (C++ new/delete operators), and does not allow you to set its default case-sensitivity state (to perform a case-insensitive comparison, it is necessary to use the CompareNoCase method explicitly). Thus, it does not fit any of the above scenarios.

STL's basic_string template class is much more flexible in this respect and allows you to create any kind of the strings suitable for specific scenarios. This is done by supplying proper char_traits and/or allocator classes as its template arguments. However, there remains one big problem. All these individual basic_string-based classes cannot cooperate with each other because the compiler treats them as distinct ones. Besides, they cannot interact either with the CString class or with the _bstr_t one. As a result, incessant explicit type casts and c_str method invocations appear in the program.

The same problem takes place with ATL/MFC CStringT template class in VC++ 7.0/7.1 (NET/NET 2003). An instance of CStringA class cannot be used together with a CStringW variable without explicit cast.

Resolving this problem was the first reason to create one more string class. The other reason is the fact that all standard VC string classes have some lack of functionality. Thus, the string content manipulation and comparison methods of the CString class yield noticeably to their STL's counterparts (find, rfind, replace, find_first_of, find_first_not_of, find_last_of, and find_last_not_of). On the other hand, the basic_string class lacks many very handy CString's methods, such as LoadString, Format/FormatV, TrimLeft/TrimRight, SpanIncluding/SpanExcluding, Mid/Right/Left, and some other.

The BasicString template class presented here is derived from the STL's basic_string template, and fills the mentioned functionality gap by providing new constructors, CString-like methods, and some new methods, which are described in the following sections. Because the the styles of CString and basic_string method names are starkly different, all the methods ported from CString, as well as some new ones, have both MFC- and STL-style names.

The second version of the BasicString class, in addition to the mentioned problems, deals with performance issues caused by using the STL's basic_string class, which became more evident after I have migrated from VC++ 6.0 to VC++ 7.1. See the Changes in Version 2 section for more detailed information.

Main Features

  1. The BasicString template class is designed to provide full interactivity between objects of all string classes based on it.

    The full interactivity means that any operator or method of any BasicString-based class accepts arguments of any other BasicString-based type. Besides, these arguments can be raw character pointers of any type (both ASCII and Unicode). Such interactivity extends over the global binary operators ( +, ==, !=, <, >, <=, >= ), too.

    For more convenience, eight different string classes that cover the most frequently used string types are typedefed in the "BasicString.h" header. They are as follows (for each of them the following information is listed): a) the character type, b) case-sensitivity of the compare method and the relational and equality operators, c) allocator type):

    • AString—char, case-sensitive, standard C++ allocator;
    • IAString—char, case-insensitive, standard C++ allocator;
    • GAString—char, case-sensitive, global COM-compliant allocator;
    • GIAString—char, case-insensitive, global COM-compliant allocator;
    • WString—wchar_t, case-sensitive, standard C++ allocator;
    • IWString—wchar_t, case-insensitive, standard C++ allocator;
    • GWString—wchar_t, case-sensitive, global COM-compliant allocator;
    • GIWString—wchar_t, case-insensitive, global COM-compliant allocator;

    Their TCHAR-based analogs are typedef'ed as well. They are:

    • String;
    • IString;
    • GString;
    • GIString;

    Additionally, the BasicString-based classes are fully interactive with the basic_string-based classes derived from the same char trait and allocator templates. For convenience, the typedefs of eight such basic_string-based classes are supplied. They are named in the same manner as the BasicString-based ones:

        AStdString (the same as STL's string class), IAStdString,
        WStdString (the same as STL's wstring class), IWStdString,
    and their TCHAR-based analogs:
        StdString, IStdString, GStdString, GIStdString.
  2. The full interactivity covers the _bstr_t and CString/CStringT classes as well. The corresponding code is compiled only if the project uses them. So, the "BasicString.h" header should be included after the <comdef.h> or <afx.h> headers. To support such functionality constructors, assignment operators, operators of conversion to _bstr_t and CString, and all binary operators (+, ==, !=, <, >, <=, >=) have been properly overloaded.

    The only known inconvenience is the impossibility to assign an object of a BasicString-based class to an object of the _bstr_t or CString type. The reason of such a behaviour is the fact that _bstr_t and CString are unaware of the BasicString class' existance, and consequently do not supply proper assignment operators. This hitch can be easily worked around by explicitly casting to the LPCWSTR or LPCTSTR as in the following example:

        String  str = _T("Test");
        _bstr_t bstr(str);      // OK
        bstr = (LPCWSTR)str;    // explicit conversion required
  3. Operators of conversion to the LPCSTR and LPCWSTR (and hence to the LPCTSTR) as well as the c_strA(), c_strW(), and c_strT() methods have been provided. They all return either the pointer to the object's own character sequence (if cast to the object's character type requested) or to the internally allocated buffer.

    Because the BasicString class derives from the basic_string, the destructor of which is non-virtual, the BasicString's destructor may not be called at some well-known circumstances. But, owing to the fact that the BasicString class does not have its own members and uses a special technique when allocating buffers to place converted strings, not calling its destructor does not cause any memory leaks.

    Yet, it must be assumed that pointers returned by the conversion operators and methods are temporary and can be invalidated by any subsequent non-const operation with the object that produced them (including, of course, its destruction, both implicit and explicit).

    Based on this functionality, the set of macros for in-place ASCII/Unicode conversion has been implemented:

        ToStr (str)     // str -> TCHAR*
        ToWstr (str)    // str -> wchar_t*
        ToAstr (str)    // str -> char*
    Here, the str argument can be char*, wchar_t*, or any kind of a string class. The pointers returned by these macros are temporary, so their use is limited to the function call argument lists or the inside of expressions they are used in.

    The second version of the BasicString class allows you to easily extend the functionality of these macros. See the Changes in the version 2 section for more detailed information.

  4. Loading strings from resources have been implemented. This is done either by means of constructor accepting raw character string pointers in the same manner as in the CString class:
        String    str((LPCTSTR)IDS_TEST);
    or by means of two STL-style methods with two their MFC-style analogs:
        bool  load ( HINSTANCE hInst, UINT nID );
        bool  load ( UINT nID );
        BOOL  LoadString ( HINSTANCE hInst, UINT nID );
        BOOL  LoadString ( UINT nID );
    The method globally changing the module handle that is used to locate resources is supplied as well:
        HINSTANCE  SetResourceHandle ( HINSTANCE hInst )
    By default, the handle of the EXE module is used to load strings. Therefore, the SetResourceHandle method should be used in DLLs to set the DLL's handle as a source of resources.

    Based on this functionality, three macros for in-place conversion from the resource ID to the raw character string pointers of different character types has been implemented:
        ResToStr (nID)
        ResToWstr (nID)
        ResToAstr (nID)
    The pointers returned by these macros are temporary, so their use is limited to the function call argument lists or the inside of expressions they are used in.

  5. Direct access to the internal string buffer is supported by means of two STL-style methods and two their MFC-style analogs:
        _E* get_buf ( size_type _Len = -1 );
        void  set_buf ( size_type _Len = -1 );
        _E* GetBuffer ( int nBufLen = -1 );
        void  ReleaseBuffer ( int nNewLen );
    Their functionality is the same as their CString counterparts have, with two following minor improvements:

    • The get_buf and GetBuffer methods can be called without a length parameter, in which case the current string length is used as the buffer size;
    • Debug versions of the set_buf and ReleaseBuffer methods perform the check of overwriting the allocated buffer boundaries. If such overwriting is detected, they assert.

  6. BSTR allocation methods analogous to those of CString class have been added. They are:
        BSTR  AllocSysString () const;
        BSTR  SetSysString ( BSTR* pbstr ) const;
    and their STL-style analogs:
        BSTR  bstr () const;    // the same as AllocSysString
        BSTR  realloc_bstr ( BSTR* pbstr ) const;
                                // the same as SetSysString
    These methods are compiled only if OLE automation headers have been included before the "BasicString.h" header file.

  7. Printf-like formatting has been implemented (using CRT library support). The following STL- and MFC-style methods do this:
        void __cdecl  printf ( const BasicString& _Fmt, ... );
        void __cdecl  printf ( unsigned int _FmtID, ... );
        void  vprintf ( const BasicString& _Fmt, va_list _args );
        void  vprintf ( unsigned int _FmtID, va_list _args );
        void __cdecl  Format ( const BasicString&
                                     strFormat, ... );
        void __cdecl  Format ( UINT nFormatID, ... );
        void  FormatV ( const BasicString& strFormat,
                                           va_list _args );
        void  FormatV ( UINT nFormatID, va_list _args );
  8. Constructing strings from GUIDs by means of the following constructor has been implemented:
        BasicString ( const GUID& );
    as well as assigning a GUID to the string:
        BasicString& operator = ( const GUID& );
    Additionally, the set of macros for in-place conversion from GUID to raw character string pointers and vice versa has been provided:
        GuidToStr (guid)
        GuidToWstr (guid)
        GuidToAstr (guid)
    The pointers returned by these macros are temporary, so their use is limited to the function call argument lists or the inside of expressions they are used in.

    To retrieve GUID from its text representation the following methods can be used:
        GUID  guid ( size_type _Pos = 0 ) const;
        GUID  GetGuid ( size_type _Pos = 0 ) const;
              // the same as guid(_Pos)
    GUID representation must start at the position specified by the _Pos argument. If there is no GUID at this position, GUID_NULL will be returned.

    If the string contains a GUID somewhere inside it, it is possible to find its start position by using the following method:
        size_type  find_guid ( size_type _Pos = 0 ) const;
        size_type  FindGuid  ( size_type _Pos = 0 ) const;
    The _Pos argument specifies the search start position. If there is no GUID found, the npos value (-1) will be returned.

    To allow for in-place conversion from any kind of a string to GUID, the following macro is supplied:
        StrToGuid (str)   // accepts any kind of a string or
                          // raw character pointers
  9. STL's comparison capabilities have been extended in the following ways. First, as it was described above, there are different classes (such as String and IString), whose relational and equality operators (==, != <, >, <=, >=) and compare method are by default always case sensitive or insensitive. The MFC-style Compare method added to the BasicString class inherits the behaviour of the compare method.
        int  Compare ( const BasicString& _S ) const;
             // restricted version of STL's compare method
    But, sometimes it could be handy to coerce case sensitivity or insensitivity of a comparison operation independently on the string class type. For this purpose, the following STL- and MFC-style methods have been added:
        int  cmp_case ( const BasicString& _S ) const;
             // always case-sensitive comparison
        int  cmp_no_case ( const BasicString& _S ) const;
             // always case-insensitive comparison
        int  CompareCase ( const BasicString& _S ) const;
             // the same as cmp_case
        int  CompareNoCase ( const BasicString& _S ) const;
             // the same as cmp_no_case
    Note   While using relational and equality operators (==, != <, >, <=, >=) with both operands being BasicString objects, the case-sensitivity of the operation is determined by the left operand. However, if one of the operands is of a raw character pointer (char* or wchar_t*), _bstr_t, or CString type, the case-sensitivity of the operation is determined by the BasicString object independently on its position. Thus in the following example:
        String str   = _T("string");
        IString stri = _T("String");
        bool b1      = str == stri;
        bool b2      = stri == str;
        bool b3      = _T("String") == stri;
        bool b4      = stri == _T("String");
    b1 becomes false, and b2, b3, and b4 become true.

  10. The CString-like Find, ReverseFind, and FindOneOf methods have been added. They all have been extended to support the second parameter specifying the start position. Besides, the ReverseFind method has been overloaded to support the string argument (CString supports TCHAR only). At last, the FindOneNotOf, FindLastOf, and FindLastNotOf methods have been added. Here are their prototypes:
        size_type Find ( _E _C, size_type _Pos = 0 ) const;
        size_type Find ( const BasicString& _S,
                  size_type _Pos = 0 ) const;
        size_type ReverseFind ( _E _C, size_type _Pos = npos )
        size_type ReverseFind ( const BasicString& _S,
                  size_type _Pos = npos ) const;
        size_type FindOneOf ( const BasicString& _CharSet,
                  size_type _Pos = 0 ) const;
        size_type FindOneNotOf ( const BasicString& _CharSet,
                  size_type _Pos = 0 ) const;
        size_type FindLastOf ( const BasicString& _CharSet,
                  size_type _Pos = npos ) const;
        size_type FindLastNotOf ( const BasicString& _CharSet,
                  size_type _Pos = npos ) const;
    Yet, the native STL's methods, that have been used to implement the preceding ones, have much broader functionality than their MFC's counterparts. These methods are (for those unfamiliar with STL): find, rfind, find_first_of, find_first_not_of, find_last_of, and find_last_not_of.

  11. The CString-like SpanIncluding, SpanExcluding, TrimLeft, TrimRight, and their STL-style counterparts have been implemented:
        BasicString substr_of ( const BasicString& _CharSet )
        BasicString substr_not_of ( const BasicString& _CharSet )
        BasicString SpanIncluding ( const BasicString& _CharSet )
        BasicString SpanExcluding ( const BasicString& _CharSet )
        void  trim ( _E _C );
        void  trim ( const BasicString& _CharSet = sm_SpaceSym );
        void  rtrim ( _E _C );
        void  rtrim ( const BasicString& _CharSet =
              sm_SpaceSym );
        void  TrimLeft ( _E _C );
        void  TrimLeft ( const BasicString& _CharSet =
              sm_SpaceSym );
        void  TrimRight ( _E _C );
        void  TrimRight ( const BasicString& _CharSet =
              sm_SpaceSym );
  12. As in CString, the FormatMessage API helpers have been included into the BasicString class. Comparing to MFC two new overloads of the FormatMessage method has been added. They accept the va_list argument list as their second parameter.
        void __cdecl format_msg ( unsigned int _FmtID, ... );
        void __cdecl format_msg ( const _E* _Fmt, ... );
        void  vformat_msg ( unsigned int _FmtID, va_list _Args );
        void  vformat_msg ( const _E* _Fmt, va_list _Args );
        void __cdecl FormatMessage ( UINT _FmtID, ... );
        void __cdecl FormatMessage ( const _E* _Fmt, ... );
        void FormatMessageV ( UINT _FmtID, va_list _Args );
        void FormatMessageV ( const _E* _Fmt, va_list _Args );
    One more major improvement is the FormatSystemMessage and FormatModuleMessage methods, which format messages that reside in the system resource tables or in a module resources correspondingly. For example, the FormatSystemMessage method can be used to obtain text descriptions for the results of GetLastError or error codes returned by COM objects.
        void  format_sys_msg ( DWORD _MsgCode, ... );
        void  vformat_sys_msg ( DWORD _MsgCode, va_list _Args );
        void  format_mod_msg ( HMODULE hInst,
              DWORD _MsgCode, ... );
        void  vformat_mod_msg ( HMODULE hInst, DWORD _MsgCode,
              va_list _Args );
        void  FormatSystemMessage ( DWORD _MsgCode, ... );
        void  FormatSystemMessageV ( DWORD _MsgCode,
              va_list _Args );
        void  FormatModuleMessage ( HMODULE hInst,
              DWORD _MsgCode, ... );
        void  FormatModuleMessageV ( HMODULE hInst,
              DWORD _MsgCode, va_list _Args );
  13. In-place character case conversion methods have been implemented. They are:
        void  to_upper ();
        void  to_lower ();
        void  MakeUpper ();
        void  MakeLower ();
  14. CString-like methods Mid, Right, and Left, that simply map to the native STL's substr method, have been added:
        BasicString  Mid ( size_type _Pos, size_type _N )  const;
        BasicString  Right ( size_type _N )  const;
        BasicString  Left ( size_type _N )  const;
    as well as the GetLength, IsEmpty, Insert, Delete, and Replace ones:
        BasicString&  Insert ( size_type _Pos, _E _C )
        BasicString&  Insert ( size_type _Pos,
                      const BasicString& _S )
        BasicString&  Delete ( size_type _Pos, size_type _N = 1 )
        BasicString&  Replace ( _E chOld, _E chNew )
        BasicString&  Replace ( const BasicString& strOld,
                      const BasicString& strNew )
    Note, that unlike their CString counterparts, the Insert, Delete, and Replace methods return not the new string length, but the reference to the string itself. This helps to build the nested string modification clauses, while the length of the resulting string is still available through the length method without any performance penalties.

Changes in Version 2

As I noted at the beginning of the article, after I migrated to VC++ 7.1 I was terrified by inefficiency of Microsoft's basic_string implementation. Implementation in VC++ 6.0 is by far not optimal as well, but it is much better than what VC++ 7.1 offers now. Let's shortly consider the flaws.

In VC++ 6.0, basic_string supports reference counting, but keeps a DWORD of string length and a DWORD of allocated string buffer size in each instance of a string object instead of keeping them only in the shared memory block together with reference count (as, for example, CString does).

Another space waste is allocator, which is a member of basic_string. Therefore, even the standard STL's allocator that has no members takes a DWORD in the string object storage. If allocator were a base class, the compiler could use empty base optimization (EBO) to not allot the memory for it at all.

As the net result, each string object in VC++ 6.0 takes four DWORDs of storage, while it could take only one DWORD (to keep a pointer to the shared memory block).

In VC++ 7.1, basic_string DOES NOT support reference counting at all, but instead each instance of the string object has a 16-byte member array for short strings. This increases the string object size up to seven DWORDs (28 bytes)!

As my tests have demonstrated, the absence of reference counting severely hits performance when strings or objects containing strings are stored in STL's containers, and this is the most frequent scenario of string usage! On the other hand, using either a member array or a dynamically allocated buffer depending on the string length requires one conditional operation for each string reference. But this badly affects huge pipelines of modern processors, and overall performance of string operations proves to be lower than if the string buffer had to be always dynamically allocated (because allocation is done once, and references to strings are normally frequent). At last, when operating with large sets of string objects, the size of the object itself becomes important. For example, vector of 1.000.000 strings (far not the biggest one for a large search system) in VC++ 7.1 requires 28 Mb while it could be only 4 Mb in size.

To improve the performance, I have modified the basic_string class from VC++ 6.0 to amend the flaws mentioned above and to make it compatible with the VC++ 7.1 iterator model. Now, BasicString class can be based not only on the native STL's basic_string, but also on its modified version contained in the header "xstring.h". Unfortunately, the compiler bug in VC++ 6.0sp5 prevents usage of the modified version with Visual Studio 6. This compiler generates incorrect code because of a special inheritance graph allowing EBO. Therefore, usage of the optimized basic_string version is suppressed in VC++ 6.0.

In VC++ 7.1, the optimized version of basic_string is used by default. To force using native STL's implementation, define the USE_CURRENT_STL_IMPL flag before including the "BasicString.h" header.

The main improvements in the BasicString class provided by the optimized basic_string version are as follows:

  • The compact object layout—the only DWORD vs. four DWORs in VC++ 6, and seven DWORDs in VC++ 7.1 (!);

  • Optimized copy/assignment implementation;

  • Optimized GetBuffer/ReleaseBuffer performance;

  • Support for maximally quick initialization of temporary string objects with a string literal via the family of _R... macros. For more information, see the comments for these macros declaration in the "BasicString.h" header.

Other improvements (not depending on the basic_string class) are:

  • Template constructor that is used to construct a BasicString object from any type, which either can be converted to pointer to underlying string element type (const _E*), or to const char*, or to const wchar_t*, or has the c_str() method.

    This constructor prevents ambiguity when the source type can be implicitly converted to both const char* and const wchar_t* (and in this case selects the most efficient way of initializing).

    Actually, this allows you to create BasicString objects from any extant string class and, potentially, from future string classes without adding specialized constructors (e.g. the first version of BasicString contained specialized conversion constructors for _bstr_t and CString, now they became unnecessary).

    The only side-effect of using this constructor is bulky compiler error messages when you try to create a string from the object of improper type.

  • Extensible ToStr/ToWstr/ToAstr macros semantics. You can make any type convertible to the String/WString/AString type by means of a simple declaration using one of the DEFINE_TYPE_TO_STRING / DEFINE_TYPE_TO_STRING_x macros. Look how this is done for RECT, POINT, SIZE, CRect, CPoint, and CSize types at the end of the "BasicString.h" header.

Incompatibilities with the first version include:

  • Definition of the BasicString class has been moved from the std namespace to std_ex;

  • Constructor taking UINT of a string resource ID is not supported anymore. Instead, cast UINT to LPCSTR or LPCWSTR explicitly before passing it to the constructor.

  • The ToTstr, ResToTstr, and GuidToTstr macros have been renamed to ToStr, ResToStr, and GuidToStr correspondingly.

  • The set_bstr method has been renamed to realloc_bstr.

Note also that I have not tested usage of the BasicString objects based on the optimized version of the basic_string class with the iostream library.

New Methods Summary

The following table comprises the new methods added by the BasicString class to its STL's basic_string predecessor. Because almost all new methods have both MFC- and STL-like names, the table has three columns. The first two contain MFC- and STL-style names of new methods, and the third column—the names of extant basic_string methods, corresponding to the added CString equivalents.

New methods and macrosExtant basic_string methods
BasicString ( HINSTANCE, UINT nStringResourceID, ... )
BasicString ( const GUID& )
operator const char* ()
operator const wchar_t* ()
operator _bstr_t ()
operator CString ()
operator CStringA ()
operator CStringW ()
operator = ( const char* )
operator = ( const wchar_t* )
operator = ( const _bstr_t& )
operator = ( const CString& )
operator = ( const GUID& )

Platform Note

Despite the fact that the BasicString class is STL-based, it violates the STL's principle of platform independence by using some NT platform-specific methods. If your target OS is not Windows, define the _NO_WINDOWS symbol before the first inclusion of this header. This will exclude the code from using Windows-specific APIs.

Additionally, find calls to the functions WideCharToMultiByte and MultiByteToWideChar and replace them with the currently commented standard C-library function calls. But note that they do not use the current system locale (at least with VC++), so if you are writing multilingual applications, you must manage locales manually using proper C-library functions.


Download source - 23 Kb


  • BasicString.h not compiling in VS2008

    Posted by bobbyalex on 06/18/2009 03:44am

    #if _MSC_VER > 1300
    	operator std::basic_string<_E1, _Tr1, _A1> () const
    		return  basic_string<_E1, _Tr1, _A1>((const _E1*)*this);//Error C2664 
    Error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const _Elem *)' : cannot convert parameter 1 from '__bs_internal__::basic_string<_E,_Tr,_A>' to 'const wchar_t *'
    I get the above error in BasicString.h while compiling in VS2008. Can anyone help?

  • An issue with arrays and assignment

    Posted by ivolucien on 08/12/2007 01:31am

    Just wanted to note that the String class attempted to deref NULL when I instantiated it in a simple array, e.g. String m_str[20], and then attempted to assign to the first String in the array. It internally called _Refcnt(_Ptr) on a NULL _Ptr (this is using the optimized xtring.h under VS 2005.) It superficially looks like you can't use it in simple arrays, though since a vector is usually an option that's not a serious issue.

    I don't have time to explore if I'm in error here, or if the class really does have serious issues with instantiation in a C-style array. This is just a heads up in case anyone runs into the problem.

    Cheers, Ivo

  • Excellent

    Posted by rajith_cherian on 04/07/2007 04:14am

    A CString code whick took 10 secs to execute now executes in 0.4 sec

  • Hmm, i'd do it differently...

    Posted by markj on 03/15/2004 08:17am

    Hmm, this string class seems to be of the kitchen sink variety, everything you could ever possibly want is thrown into the one class. The problem with this approach is that it mixes the concerns of multiple domains. For example, the simple trim functionality. If this is done properly, it needs to be locale aware. However, you don't really want to tie this into your string class. Instead you should have some external functions to do the job for you and not couple the class to the OS. To cap it all off, i'd even question the creation of a string class in the first place! An array class is sufficent with a couple of overloads to cater for null terminated strings.

  • Another STL BSTR

    Posted by Legacy on 02/05/2004 12:00am

    Originally posted by: Jim Cohen

    I have found another STL implmentation on BSTR you may want to check up on internet: http://home.comcast.net/~georgesalnikov/CodeSamples/cpp_string.html

  • Great

    Posted by Legacy on 11/05/2003 12:00am

    Originally posted by: Francisco Campos

    Excellent!!, I'll use in my new library

  • I like it

    Posted by Legacy on 07/28/2003 12:00am

    Originally posted by: Richard

    There are certain apps I maintain that are a mix of (MFC, ATL, STL) CString, and BSTR types with a couple of stl strings in there.. lots of dumb conversions going on there.. this class is a great solution for this scenario.

  • hmm

    Posted by Legacy on 05/16/2003 12:00am

    Originally posted by: John Torjo

    1. you are not allowed to extend the std namespace

    2. usually, you should not derive your custom class from std::basic_string

    3. Chartraits.h - you only assume that it will be used for (signed/unsigned)chars/ wchar_ts. What about other chars?

    at compare, in case _N is 0, the code is totally bogus.
    As a matter of fact, std::strings can contain embedded '\0' chars; so therefore you should not query about a string's length using lstrlenA/lstrlenW
    - because you don't know the string's length

    4. too many typedefs AIString, AString, etc.

    5. you assume that the STL that comes with VC6 is used; I'm sure your code won't work with STLPort, lets say, since you're using the nonportable _Myt - there's nowhere in the C++ standard any reference to _Myt within the context of std::basic_string. You could have eventually had something like your own typedef:
    typedef std::basic_string<_E, _Tr, _A> string_type;

    I'm not sure why you overloaded the find, rfind, etc. functions, if all you did was call the base class.

    6. actually, for your BasicString class as well, you assume that it will be used only for chars and wchar_ts.

    7. you actually provide c_str*() functions, and implicit conversions. This is BAD, and could lead to ambiguities.
    For starters, it's almost impossible to use it in any generic programming.
    Consider this:

    void get_len( const char * s) { return strlen(s); }
    void get_len( const wchar_t*s) { return wstrlen(s); }

    AString s;
    get_len(s); // ambigous !!!

    8. Your class wants to be everything to everybody, and generally speaking, reality has shown that it's a bad thing.

  • Thanks

    Posted by Legacy on 04/30/2003 12:00am

    Originally posted by: Shyan Lam

    Good job :-D and thanks for the source.

  • Great stuff

    Posted by Legacy on 04/24/2003 12:00am

    Originally posted by: Peter Kohout

    I've just finished a small project where I was constantly changing from CString and string. Wish you posted this earlier!!! Your hard work and sharing is much appreciated.
    Keep it up



Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

  • "Security" is the number one issue holding business leaders back from the cloud. But does the reality match the perception? Keeping data close to home, on premises, makes business and IT leaders feel inherently more secure. But the truth is, cloud solutions can offer companies real, tangible security advantages. Before you assume that on-site is the only way to keep data safe, it's worth taking a comprehensive approach to evaluating risks. Doing so can lead to big benefits.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds