Introduction
Its been almost 5 months since I'm involved in a very large Visual C++
project. The thing that I missed the most was a really practical string
class.
<Getting political>
A good programmer is one that
gets things done on time. Getting things done on time depends on the language
you use and the available libraries. It's obvious that the language we use is
C++, more exactly Visual C++. C++ is probably the most difficult language to
master these days but we can't do anything about it, that's the standard and we
have to live with it, and so we have a -(minus) on the "gets things done in
time", which is almost always true from the point of view of management people.
The only thing that can save us, besides years of experience, is good library
support. The specific library support I will talk about in this article is, of
course, string related and the keyword is practical.
On the MFC side I have CString
, it's a decent string class
implementation but I'm not very happy with it mainly for three reasons:
- I can use it only in MFC projects
- I cannot chain operations on a
CString
instance. Example: CString str( " 05 09 1990 ");
str.TrimLeft();
str.TrimRight();
str.Replace( " ", "/" );
By chaining operations I mean the following:
str.TrimLeft().TrimRight().Replace( " ", "/" );
See the difference? I don't know about you but I definitely do and I don't
want to go back.
- Numeric operations support. This is something I used even in the smallest
projects I've done. There's almost no way I can escape these conversion
operations, and again
CString
doesn't do a good job to help me
out. Of course CString
provides Format
and
FormatV
methods but they're not at all nice with me.
On
the non-MFC side I have ( if you're a STL lover stop reading here )
well...almost nothing. Yes, I know there is
std::string
but I can't
do much with it. It suffers the same drawbacks ( maybe more ) all STL does: too
much
academicism as oposed to practical, everything is so in place and
standards aligned that I am nothing but delighted when I look at it.
Unfortunately that's all about it.
</Getting political>
Using the code
The class name is CStr
. I'll start directly with examples, I
think the class interface is self-explanatory:
Chaining operations: The above CString
example using the
CStr
class:
CStr str( " 05 09 1990 " );
str.Trim().Replace( " ", "/" );
Numerical operations:
CStr str;
long lValue;
str = 123;
lValue = str;
double dValue;
str.SetDouble( 123.45 );
dValue = str.GetDouble();
Append operator ( denoted by << ) : Using this operator you can chain
concatenation like this:
CStr str;
str << "Value=" << 12 << .5 << " seconds";
Observations:
- It is not the perfect string class but I like it, there will be bugs for
sure in this implementation, so please let me know. If you think there are
methods that must be added/changed also let me know, and if I like the
proposals I'll change the interface.
- In many cases are performed operations that modify the string length, and
thus the size of the internal buffer, and to avoid some of memory reallocation
cycles each new allocation adds an extra buffer size of
CSTR_EXTRA_SIZE
(defined as static member). The default value is 16, if you don't want to use
this feature and want to keep the buffer as small as possible set
CSTR_EXTRA_SIZE
value to 0.
- Numerical conversion regarding double values is done using
_vsntprintf
and
setting type field to "%g".
- Though I wrote this class with full Unicode support I didn't have a chance
to test it using Unicode, so it'll be great if someone can test it, before
I'll use it in real projects ;).
- The demo project is an empty MFC project that is full of
ASSERT
s that wrap CStr
methods for testing,
something like unit testing.
CStr public Interface
static int CSTR_EXTRA_SIZE;
CStr() throw();
~CStr();
CStr( const CStr &str ) throw();
CStr( LPCTSTR pszStr ) throw();
CStr( int allocSize ) throw();
CStr& operator = ( const CStr& str ) throw();
CStr& operator = ( LPCTSTR pszStr ) throw();
CStr& operator = ( int iValue ) throw();
CStr& operator = ( long lValue ) throw();
CStr& operator = ( unsigned long ulValue ) throw();
CStr& operator = ( double dValue ) throw();
TCHAR* Buffer() const;
TCHAR* Buffer();
int BufferSize() const;
BOOL Realloc( int size ) throw();
void Compact() throw();
int Length() const;
BOOL IsEmpty() const;
BOOL operator == ( LPCTSTR pszStr ) const;
BOOL operator != ( const LPCTSTR& str ) const;
int Compare( LPCTSTR pszStr ) const;
int CompareNoCase( LPCTSTR pszStr ) const;
operator LPCTSTR () const;
operator int () const;
operator long () const;
operator unsigned long () const;
operator double () const;
TCHAR& operator [] ( int pos );
const TCHAR& operator [] ( int pos ) const;
CStr Left( int size ) const throw();
CStr Right( int size ) const throw();
CStr Mid( int start, int size ) const throw();
int FindCount( LPCTSTR pszStr ) const;
int FindCount( int startPos, LPCTSTR pszStr ) const;
int Find( LPCTSTR pszStr ) const;
int FindNth( int nth, LPCTSTR pszStr ) const;
int ReverseFind( LPCTSTR pszStr ) const;
int ReverseFindNth( int nth, LPCTSTR pszStr ) const;
CStr& Empty();
CStr& Fill( const TCHAR chr );
CStr& Trim();
CStr& TrimLeft();
CStr& TrimRight();
CStr& Lower();
CStr& Upper();
CStr& Insert( int pos, LPCTSTR pszStr ) throw();
CStr& Prepend( LPCTSTR pszStr ) throw();
CStr& Append( LPCTSTR pszStr ) throw();
CStr& Remove( int pos, int len ) throw();
CStr& Trunc( int pos );
CStr& Replace( LPCTSTR pszOld, LPCTSTR pszNew ) throw();
CStr& Replace( int startPos, LPCTSTR pszOld, LPCTSTR pszNew ) throw();
CStr& Format( LPCTSTR pszFormat, ... ) throw();
CStr& FormatV( LPCTSTR pszFormat, va_list args ) throw();
int GetInt() const;
long GetLong() const;
unsigned long GetULong() const;
double GetDouble() const;
CStr& SetInt( const int iValue ) throw();
CStr& SetLong( const long lValue ) throw();
CStr& SetULong( const unsigned long ulValue ) throw();
CStr& SetDouble( const double dValue ) throw();
friend CStr& operator << ( CStr &str, LPCTSTR pszStr ) throw();
friend CStr& operator << ( CStr &str, int iValue ) throw();
friend CStr& operator << ( CStr &str, long lValue ) throw();
friend CStr& operator << ( CStr &str, unsigned long ulValue ) throw();
friend CStr& operator << ( CStr &str, double dValue ) throw();
That's it. Have fun !
History
June 21, 2003
- Replaced
new/delete
operators with malloc/free
for internal buffer allocation/deallocation.
It seems malloc is faster for large arrays.
- If memory allocation fails the
std::bad_alloc
exception will be thrown. The methods that may throw this exception
are marked with throw()
in the class declaration.
- As requested two new functions were adeded:
FindNth( int nth, LPCTSTR pszStr ) const
which returns the zero-based index of the nth occurence of pszStr. ReverseFindNth( int nth, LPCTSTR pszStr) const
does the same thing only doing reverse search starting from the end of the string.
-
Contains
method was renamed to the more suggestive FindCount
.
- All
#define
s replaced with static members, including CSTR_EXTRA_SIZE
.
- The
CStr
implementation was backed up by _ASSERT
s.
- Other small cleanups.