Click here to Skip to main content
Click here to Skip to main content

CString-clone Using Standard C++

By , 7 Dec 2011
 

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:

// 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)

About the Author

Joe O'Leary
Web Developer
United States United States
Member
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

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: std::isspace in VC6 returns true for international characters like ÖmemberKarl Edwall4 Mar '12 - 18:22 
Yeah seems to be a rounding issue of some kind, if you change it to (int) or (CHAR), it works fine but seeing the UNICODE one is basically basic_string probably why it happens.
 
Karl
QuestionBest string code ever - with memory shortagememberBostjan Erzen24 Jan '12 - 5:14 
Hi,
 
this is one of THE best piece of code concerning strings and C++. I currently use MFC and formatting is around 100% faster with this code. However there is one shortage. MFC CString only uses 4 bytes when holding no string, while CStdString uses 28. When using it in large arrays this makes big difference. Is there a way to optimize this (without sacrifice anything else)?
 
Best regards,
Bostjan Erzen
AnswerRe: Best string code ever - with memory shortagememberJoe O'Leary24 Jan '12 - 5:47 
Hi Bostjan,
 
I'm glad you like the code,
 
Unfortunately, this memory footprint is something over which I have no control. My code adds no memory to the base class (basic_string) and performs no allocations for an empty string. Those 28 bytes (I think they're 32 bytes in release mode...) are completely allocated by Microsoft's own implementation of the basic_string template.
 
They chose an approach that gives each string object a small, internal buffer, by default. The idea was to minimize memory allocations for short strings. While it certainly helps in that case, it creates a big default size. You can read about the issue here:
 
http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/9a59970d-c7bf-4ed5-8267-e482c4e461a7/[^]
 
The only way I can think of to get around this is to use an alternate implementation of the Standard C++ Library, like, perhaps the STLPort implementation - http://www.stlport.org
 
Sorry. I really have no ability to get around this problem. I can't edit the base class code (nor would anyone want me to!)
 
-Joe
-Joe

QuestionNo way to compile it :(memberDeepButi27 Dec '11 - 10:39 
Just what I was looking for ... but no way to make it work. Using a Windows/7 system.
 
First it asked me for crtdbg.h, which I don't have. Google tells me to install MicroSoft SDK.
I tried ... but it asks for Visual C++ 8 SP1.
I tried installing VC 8 SP1 but it asks for a full version of VC 8 (not Express), so this is not free software.
 
And I thought that the whole class didn't need any previous VS! Frown | :(
 
Found a crtdbg.h and copied it to the local folder.
 
Now I get dozens of errors (deleted all warnings):
 
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h||In function 'int ssvsprintf(WCHAR*, size_t, const WCHAR*, char*)':|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|1673|error: invalid conversion from 'size_t' to 'const wchar_t*'|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|1673|error: cannot convert 'const WCHAR*' to 'char*' for argument '3' to 'int vswprintf(wchar_t*, const wchar_t*, char*)'|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h||In member function 'void CStdStr<CT>::FormatV(const CT*, char*)':|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3183|error: 'size_type' was not declared in this scope|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3183|error: expected ';' before 'nActual'|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3191|error: there are no arguments to '_alloca' that depend on a template parameter, so a declaration of '_alloca' must be available|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3191|note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3196|error: 'nActual' was not declared in this scope|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3204|error: 'nActual' was not declared in this scope|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h||In member function 'void CStdStr<CT>::FormatMessageA(typename std::basic_string<_CharT, std::char_traits<_CharT>, std::allocator<_CharT> >::const_pointer, ...)':|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3310|error: 'runtime_error' is not a member of 'std'|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h||In member function 'void CStdStr<CT>::FormatMessageA(UINT, ...)':|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3329|error: 'runtime_error' is not a member of 'std'|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h||In member function 'OLECHAR* CStdStr<CT>::SetSysString(OLECHAR**) const':|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3571|error: 'runtime_error' is not a member of 'std'|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h||In member function 'void CStdStr<CT>::CharToOemA()':|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3599|error: there are no arguments to 'empty' that depend on a template parameter, so a declaration of 'empty' must be available|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h||In member function 'void CStdStr<CT>::OemToCharA()':|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3612|error: there are no arguments to 'empty' that depend on a template parameter, so a declaration of 'empty' must be available|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3880|error: there are no arguments to 'SSResourceHandle' that depend on a template parameter, so a declaration of 'SSResourceHandle' must be available|
C:\Users\Enric\Documents\ButiNET\Test\stdstring.h|3884|error: there are no arguments to 'SSResourceHandle' that depend on a template parameter, so a declaration of 'SSResourceHandle' must be available|
 
Any idea?
 
Thks
AnswerRe: No way to compile it :(memberJoe O'Leary27 Dec '11 - 11:26 
First let me figure out what you are using. What compiler are you using? Visual C++? If so, what version? 2008? 2010? 2005?
 
I have all 3 of those on my box. If you are having troubles, can you send me a zipped up copy of the test project you are using? I should be able to figure it out
-Joe

GeneralRe: No way to compile it :(memberDeepButi27 Dec '11 - 21:07 
No, I'm not using any VC version.
 
I never installed VC on my W/7 computer, but for some reason there are several VC5/VC8 redistributables on the list of installed programs. I tried to install VC 2010 Express but it fails, Google says "uninstall VC8" but I cannot unistall any of them, all unistall attempts end up with error: either windows install service unavailable, or install source unavailable. So seems I'm blocked somehow Frown | :(
 
I use Code::Blocks with GNU GCC compiler. I just created a new project with nothing else than the stdstring.h and a main.
GeneralRe: No way to compile it :(memberJoe O'Leary28 Dec '11 - 3:21 
Ah, GNU. OK,
 
Well first of all, to get around the code trying to include crtdbg.h, tell it to go ANSI-only by adding the following line somewhere near the top of the file
 
#define SS_ANSI
 
If you still have problems with the vsnprintf function showing up in your output, take a look at the comments I put there. The function seems to have different signatures depending on the platform. I have some conditional #ifdefs around it, trying to make it select the right one based off of the platform but those are far from perfect.
 
I'm afraid I can't help you with the uninstalling of Visual C++
-Joe

GeneralRe: No way to compile it :(memberDeepButi28 Dec '11 - 7:23 
Solved.
The #define gets rid of the crtdbg problem.
 
I changed
 
// Enric 281211 return vswprintf(pW, nCount, pFmtW, vl);
return vswprintf(pW, pFmtW, vl);
 
and everything seems to work.
 
Ten points for your code!
 
Thks
GeneralRe: No way to compile it :(memberJoe O'Leary28 Dec '11 - 3:27 
If you need to email me directly, my email address is the earthlink one at the top of the header file. It still works. I've had it for 16 years!
-Joe

GeneralGreat CodememberJeffBilkey14 Dec '11 - 14:41 
Top piece of code. Been after something like this for years. Works perfectly on Embarcadero 2010. I take it I look at CString http://msdn.microsoft.com/en-us/library/aa252634(v=VS.60).aspx[^] for the manual.

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 7 Dec 2011
Article Copyright 2001 by Joe O'Leary
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid