Click here to Skip to main content
15,898,134 members
Articles / Programming Languages / C++

CString-clone Using Standard C++

Rate me:
Please Sign up or sign in to vote.
4.93/5 (128 votes)
7 Dec 2011CPOL4 min read 4M   14.1K   227   568
A Drop-In replacement for CString that builds on the Standard C++ Library's basic_string template

Introduction

As much as I use and appreciate the Standard C++ Library, I've never liked its string template - basic_string<>. At times, it seems the designers went out of their way to make it difficult to use.

On the other hand, I've always loved the ease of use of MFC's CString class. It checks for NULL pointers, implicitly converts to const TCHAR*, and has some very handy member functions (Format, Load, etc.) that make string programming a breeze. But of course, I don't want to use MFC anymore. In fact, I don't want to rely on any proprietary library because I want portability.

Therefore I decided to combine the best of both worlds and create:

CStdString

This is a class (a template instantiation actually) that derives from from basic_string<TCHAR>. To the basic_string it adds the entire CString API. You get CString ease of use with 100% basic_string compatibility. In short, a CStdString object is a basic_string that (with very few exceptions (noted below) it is also a drop-in replacement for CString. The best part of this is that both APIs (basic_string and CString) are well known and well documented.

I originally submitted this article to another code site (which shall remain nameless :)) a few years ago. I like CodeProject so much I thought I'd submit it here too. I have used this class in almost every professional project I've done over the past 4 years. It has proven to be the single most useful piece of code I've ever written. It is also extensively debugged. I hope you like it. If you ever have any problems with it, please e-mail me. I'm happy to help.

I provided a simple source application here to prove some of the CString functions work but it's really just a token. The list of sample projects out there that use CString and/or basic_string is massive.

Features

  • Drop in Replacement for CString (see below for exceptions)
  • Two instantiations available at all times -- wchar_t-based version CStdStringW and char-based version CStdStringA. The name CStdString is just a typedef of one of these two.
  • Safely checks for NULL string pointer inputs (like CString) in all functions
  • Extra constructors and assignment operators to automatically convert between wide (wchar_t-based) and thin (char-based) strings for you.
  • Implicit conversion to c_str(). The C++ committee doesn't like this but I sure do.
  • Builds on several platforms, including Windows, Unix and Linux. Works with several implementations of the Standard C++ Library, including Dinkumware, GNU, CodeWarrior, and STLPort.
  • Win32 builds give you some extra goodies like UNICODE/MBCS conversion macros (just like MFCs) as well as member functions for persisting CStdString objects to and from DCOM IStreams.
  • Makes no use of any implementation details of the base class template (basic_string)
  • The derived template adds no member data to basic_string and adds no virtual functions

There are a couple of issues about this code of that I should point out.

CString Compatibility

I was unable to exactly reproduce the CString API. There are a two functions that both CString and basic_string; share, but implement differently. In these cases, I felt it best to make CStdString behave like basic_string (the base class) rather than CString. To be specific.

  • CStdString::operator[] returns characters by value (unlike CString which returns them by reference)
  • The constructor that takes a character and a count takes them in the order (count, value) which is the opposite of the order CString declares them. That's the order that basic_string<>; needs and it was impossible to implement both versions.

There were also two CString functions I could not implement at all -- LockBuffer and UnlockBuffer.

Deriving From basic_string<>

The template I wrote derives from basic_string, a class template without a virtual destructor. Any introductory text to C++ will tell you that it is dangerous to derive from a class without a virtual destructor. It can lead to behavior that is undefined. So if you were to code the following (deleting a CStdStringA through a pointer to the base class), you would technically get undefined behavior:

C++
// assign DERIVED object to  BASE pointer
std::string* pstr = new CStdStringA("Hi"); 

// delete  DERIVED through BASE class pointer -- UNDEFINED
delete pstr;   

Personally, I don't think this is much of an issue. I mean really how often do you actually do this with string objects? I have rarely (if ever) needed to dynamically allocate a string object on the heap. And if I ever do, I won't using a base-class pointer. So if you don't do this, you'll never have to worry. In fact, even if you do code this way, I doubt you'll have any problems with CStdString. I can tell you that at least with Microsoft Visual C++, even the above code runs just fine with no errors or memory leaks. I doubt many other compilers would give you problems either. However my doubt does not impose reality on the C++ world. Caveat Emptor.

History

  • 7 Dec 2011: Updated source code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
I've been a software developer since 1990.

While my main page is out of date (and I have therefore blanked it), you can read about the CStdString pages here

http://home.earthlink.net/~jmoleary/stdstring.htm

Comments and Discussions

 
Generalmy two cents... Pin
Philippe Bouteleux10-Jun-03 6:50
Philippe Bouteleux10-Jun-03 6:50 
GeneralRe: my two cents... Pin
Philippe Bouteleux10-Jun-03 6:53
Philippe Bouteleux10-Jun-03 6:53 
GeneralRe: my two cents... Pin
Joe O'Leary10-Jun-03 7:55
Joe O'Leary10-Jun-03 7:55 
Generalvswprintf preprocessor checks Pin
billca3-Jun-03 3:33
billca3-Jun-03 3:33 
GeneralExcellent -- this I LIKE Pin
Joe O'Leary3-Jun-03 18:01
Joe O'Leary3-Jun-03 18:01 
Generalvswprintf format spec Pin
billca3-Jun-03 3:17
billca3-Jun-03 3:17 
GeneralRe: vswprintf format spec Pin
Joe O'Leary3-Jun-03 17:54
Joe O'Leary3-Jun-03 17:54 
GeneralRe: vswprintf format spec Pin
Anonymous4-Jun-03 10:39
Anonymous4-Jun-03 10:39 
For what it's worth, I too have thought all along that you probably shouldn't change any code for this, but to me the issue is perhaps a bit more significant than I gather you're thinking, and noteworthy (whether for documentation or just in your mind) if only because I think others will inevitably and innocently fall into this trap. I probably didn't explain my thoughts clearly enough at first as to why I think it's significant -- or at least interesting. Not to try to persuade any action on your part, but just as explanation and at the risk of boring you...

As I said, although you don't claim that CStdString is exactly the same as CString, the fact is that one can take an MFC application, change the CStrings to CStdStrings and (aside from some possible tweaks for compilation purposes) run it on UNIX -- this is what makes CStdString so great. Other than the StdCodeCvt problem you and I are currently discussing elsewhere, this is the only runtime issue I have encountered related to CString/CStdString replacement that requires changes to my applications (well, since I decided not to use SS_SAFE_FORMAT, I also had some changes to make related to that), which alone makes this issue special. And when first addressing the issue, it's particularly hard to figure out -- there's no crash, the strings just don't get formatted correctly. Considering it purely from this angle -- as a replacement for CString -- it is very much like your SS_SAFE_FORMAT issue, as you pointed out.

With the vs(n)printf and vs(n)wprintf functions on Windows, which I assume the CString Format function uses or at least emulates, '%s' means a thin string and '%S' means a wide string for vsprintf, but the opposite for vswprintf where '%s' means wide and '%S' thin! Apparently CString.Format works the same way. So with UNICODE defined, '%s' means the argument is a *wide* string. And with UNICODE not defined, the same symbol, '%s', means *thin* string. So the application code stays the same (i.e. lower case 's') whether built for UNICODE or not, which is nice. The only time you would use '%S' with CString is when you had a string argument that is opposite in thinness/wideness from the CString itself. Incidently, while the point made by your counter example is well-taken, the implication is that the example would not be correct if mystring1 were a UNICODE-enabled CString (the format symbols would need to be reversed) -- maybe you realize this and didn't intend to say anything to the contrary.

Now with CStdString -- and running on UNIX (Solaris, at least) -- this is NOT the case. Granted, CString doesn't even work at all on UNIX (otherwise I suppose it would have to deal with this same issue), so understandably CStdString is taking on a much bigger burden. But because the underlying vswprintf function on UNIX (unlike the Windows version, for whatever reason) expects '%S' for wide strings, all the Format '%s's in a UNICODE-enabled application would need to be changed to '%S'.

Now I chose to leave all the Format '%s's in my application code as lower case and programmatically convert them to '%S' inside the ssvsprintf function (as well, I suppose, for consistency change any '%S's to '%s'). But as you pointed-out of course this only works when you assume there are no Format calls, in a UNICODE-enabled application, that include a thin string argument with the "correct" (correct for UNIX, that is) symbol, '%s'. For me this won't happen because all my CStdString.Format calls (mostly former CString.Format ones -- and a few new ones) are coded the CString way. So in the unlikely event I ever did include a thin string argument, I would use the '%S' symbol and it would ultimately be handled correctly.

And so my point is simply that this issue, which I tend to think would be encountered by many, and the subsequent changes required, is significant because it is really the one and only hindrance to what is otherwise a practically flawless CString/CStdString migration.

Bill
GeneralError on StdCodeCvt calls Pin
billca2-Jun-03 10:11
billca2-Jun-03 10:11 
GeneralRe: Error on StdCodeCvt calls Pin
Joe O'Leary3-Jun-03 17:42
Joe O'Leary3-Jun-03 17:42 
GeneralRe: Error on StdCodeCvt calls Pin
billca4-Jun-03 6:27
billca4-Jun-03 6:27 
GeneralRe: Error on StdCodeCvt calls Pin
billca3-Jul-03 9:41
billca3-Jul-03 9:41 
GeneralYou are correct Pin
Joe O'Leary3-Jul-03 13:55
Joe O'Leary3-Jul-03 13:55 
GeneralSlight problem with Dinkumware STL libs 3.08 ... Pin
Robert Ibsen Voith28-May-03 2:26
Robert Ibsen Voith28-May-03 2:26 
GeneralError during compilation VS .Net 2003 Pin
Kurt _B11-May-03 6:30
Kurt _B11-May-03 6:30 
Generaluse newest vsersion Pin
Baryon Lee12-May-03 19:02
Baryon Lee12-May-03 19:02 
GeneralLOL! Pin
Joe O'Leary12-May-03 19:31
Joe O'Leary12-May-03 19:31 
GeneralCompile error on HP-UX Pin
billca30-Apr-03 3:52
billca30-Apr-03 3:52 
GeneralRe: Compile error on HP-UX Pin
Joe O'Leary9-May-03 10:08
Joe O'Leary9-May-03 10:08 
GeneralRe: Compile error on HP-UX Pin
billca13-May-03 3:38
billca13-May-03 3:38 
GeneralRe: Compile error on HP-UX Pin
billca13-May-03 3:42
billca13-May-03 3:42 
GeneralRe: Compile error on HP-UX Pin
billca13-May-03 3:56
billca13-May-03 3:56 
GeneralRe: Compile error on HP-UX Pin
Joe O'Leary13-May-03 4:11
Joe O'Leary13-May-03 4:11 
GeneralRe: Compile error on HP-UX Pin
billca13-May-03 8:39
billca13-May-03 8:39 
GeneralRe: Compile error on HP-UX Pin
billca3-Jun-03 3:41
billca3-Jun-03 3:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.