|
|
Comments and Discussions
|
|
 |

|
If this is not safe, why not just check the memory address, and then allocate a temp buffer for the user? Or just always allocate a temp buffer?
GetBuf()
if ( static_cast<int>(this->size()) < nMinLen )
this->resize(static_cast<MYSIZE>(nMinLen));
if ( (&(this->at(length())) - &(this->at(0))) == length() * sizeof(TCHAR))) return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
else
{
if (nMinLen < length()+1)
nMinLen = length()+1;
_tempbuffer = new TCHAR[nMinLen];
_tcscpy(_tempbuffer,this->data());
return _tempbuffer;
}
RelBuf
{
if (NULL != _tempbuffer)
{
*this = _tempbuffer;
delete [] _tempbuffer;
tempbuffer = NULL;
}
this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :sslen(this->c_str())));
}
modified 15-Apr-13 7:32am.
|
|
|
|

|
To quote my answer to your other post on this question below:
Quote: I'm afraid this would not be a solution. You can't just return a buffer that you have allocated via operator new. The caller is not expecting to have to free any buffer returned to him/her. So you would end up with a memory leak in this case.
And, while checking addresses might reduce the chance of my "hack" coming back to bite you, it would not eliminate it, I'm afraid. Of course it could only bite you in the mythical implementation that uses non-contiguous buffers but if you actually had such an implementation, there is still the possibility that intermediate characters are not at memory locations between the first and last.
-Joe
|
|
|
|

|
Great work! BTW CString is also part of ATL
|
|
|
|

|
Nice work! This could be very useful for getting CString in a non-MFC environment. Thanks for the article!
|
|
|
|

|
Great class. Thank you! My vote of 5. Suggestion: How about using _vscprintf/_vscwprintf function, when calculating resulting buffer size in CStdStr<T>::FormatV? I don't know whether compilers other than Visual C++ supports this function, but Visual C++ supports _vscprintf/_vscwprintf.
|
|
|
|

|
Personally, I don't see what is so great about CString let alone std::string. Why were you even making std::string* str = ...? std::strings don't need to be pointers or any string class as they are shared classes. Personally, if you are going to bother making any decent string class. I wouldn't waste time using crappy hungarian notation. If you're even going to get any other developer on a OS besides windows. They will not hungarian notation as it serves no purpose in this modern world. Look at C#. They do not even use it and never should you. That being said. I would recommend you looking at Qt's QString class which offers more than enough of a string class that is 1 million times better than CString and std::string.
Source code is ugly too. I suggest you splitting up the the classes and use type_traits based on how you're going to use it, which will make the code less ugly and more readable. Use proper parameter names too.
|
|
|
|

|
I don't even understand the first part of your post. You wrote this:
Quote: Why were you even making std::string* str =
I agree that no one should use that. I never said anyone should. So what are you objecting to?
As for not liking Hungarian notation, well that ship has sailed for most folks. Too many are already working in code bases that have Hungarian. I merely followed suit.
And I have used QT's string class. I spent two years dealing with that. It's fine if all you do is live in the world of QT (which uses the Hungarian Notation you object to, by the way). Otherwise, No thanks.
If you object for the formatting, perhaps you could rewrite the code for us to suit your aesthetic sensibilities. That's the great thing about free code. You can rewrite it, repost it to suit your needs.
-Joe
|
|
|
|

|
Btw it's Qt not QT. QT is Quicktime. Qt isn't an acronym. Where is Hungarian notation anywhere in Qt? now, I don't know about internal, as I don't mess with that. Just saying, it's useable outside of Qt too, if you actually wrote a class that performed the same functionality. Why would I bother rewriting code you wrote? That's your job not mine. I personally have no use for the code. I'm not saying your code is bad as I haven't used it. I've only glanced over it and gave my opinions.
|
|
|
|

|
Yes and I gave mine. Thanks for sharing
-Joe
|
|
|
|

|
Quote: Why were you even making std::string* str = ...? std::strings don't need to be pointers or any string class as they are shared classes.
You're missing the point. The intent was a heads-up warning that CStdString doesn't support being polymorphically deleted through it's base class.
Steve
|
|
|
|

|
If you bothered reading the article. I was was referring to this:
// assign DERIVED object to BASE pointer
std::string* pstr = new CStdStringA("Hi");
// delete DERIVED through BASE class pointer -- UNDEFINED
delete pstr;
Which you should never make a std::string or any string class.
|
|
|
|

|
I did read the article, and reviewed the code, and I was referring to that too! My comment stands: since basic_string doesn't have a virtual destructor it's not safe to delete a CStdString polymorphically via a pointer to a basic_string. The author wasn't presenting a design pattern but warning users about a limitation.
Steve
|
|
|
|

|
Okay, then ignore that. :P
|
|
|
|

|
If you don't like Hungarian notation or CString then just don't use this class. Seems like it does provide a use for those who are familiar with and/or want to use CString in a non-MFC environment. Complaining because you don't like hungarian notation or think its "modern" is a pretty cheap shot and not all that constructive imo. I use CString a lot and I think this could be a very useful tool in a non-MFC environment.
|
|
|
|

|
The following is an excerpt from your code:
CT* GetBuf(int nMinLen=-1)
{
if ( static_cast<int>(size()) < nMinLen )
resize(static_cast<MYSIZE>(nMinLen));
return empty() ? const_cast<CT*>(data()) : &(at(0));
}
It is not legal or portable to cast away the const and modify the buffer, nor can you assume that &at(0) == &at(1)-1. What's more, the assumption that these illegal modifications to the buffer will actually modify the underlying string is also illegal and implementation defined. For example, it is legal for a basic_string to use non-contiguous memory regions (and doing so has advantages) for it's underlying storage. In this case the buffer returned by c_str and data is created on demand and changes made to it need not be copied back to the string's real buffer and &at(0) may or may not be equal to &at(1)-1 .
The reason for c_str and data is to support interfacing with legacy code and the reason they return const pointers is to allow basic_string the flexibility to use such optimisations.
Steve
modified 5-Oct-12 7:10am.
|
|
|
|

|
You are correct. The buffer managed by basic_string need not be contiguous and if it were not, GetBuffer() and its variations would simply not work. There is, of course, no other way to implement CString's GetBuffer functionality as it depends specifically upon this behavior. I understood that when I first wrote the code, roughly 16 years ago.
I finally decided to "risk" this and use my implementation because I was unable to find any implementation of std::basic_string that did *not* use a contiguous buffer. And now, 16 years later, I still cannot name such an implementation. Can you?
-Joe
|
|
|
|

|
I don't have a vast array of C++ compilers or STL versions, so I can't really comment on that. You are presenting the code as standard C++ however, both in the article and in the code's comments, so I would suggest that this section of the code warrants both a comment in the source and a heads up in the documentation, and there's an argument that if it's present at all you should be able to control its inclusion with a macro.
Steve
|
|
|
|

|
Neither do I. I've tested it with a few (STLPort, gcc, Borland back in the day, etc) and have heard from more people using more compilers/tools/implementations than I can count. I do not believe such an implementation exists. Not because it could not be done, but because it sounds better in theory than it works in practice. To wit: the buggy "optimized" implementation that Dinkumware put out for MS originally that used--as I vaguely recall-- broken "copy on write" semantics that fell over in multithreaded scenarios. They didnt even try to fix that. They just got rid of it in a hurry.
When I first posted this class on another board I actually did not have a GetBuffer implementation at all (just as there is still no LockBuffer imementation). And it was for that very reason: There is no way to do it without unsafe code. The few other CStdString functions that use it used to new/delete buffers and then assign instead. But so many people kept asking me for it that I wrote one. Over the years, I have never felt the need to replace it. My philosophy since has been caveat emptor. There are a couple of aspects of this code for which that attitude is necessary ( e.g. deriving from a class without a virtual destructor.). If you think GetBuffer is scary, take a look at the code gymnastics I go through in Format to protect people from a Microsoft implementation hack. Its safer but in the end you can only go so far.
In the end I view this class as a way for people to wean themselves off of CString and functions like GetBuffer, Format and other dicey operations it provides in favor of the basic_string equivalents, stringstreams, etc. Beyond that, its main utility is in the conversions, NULL checking, and few Win32 wrappers it provides that simplify string programming.
This class does not "present itself as standard". I have implemented the CString API as best I can using basic_string functions but that implementation is necessarily far from perfect. Rest assured,I will not be submitting this to ISO anytime soon.
i am not even a little bit worried that anyone is ever going to run into an implementation of the library that breaks my version GetBuffer, but if/when I ever get around to posting my local version (that fills in a few missing CString functions) I will add both a preprocessor macro and a comment for GetBuffer. I will leave it enabled by default, however.
-Joe
|
|
|
|

|
If this is not safe, why not just check the memory address, and then allocate a temp buffer for the user? Or just always allocate a temp buffer?
GetBuf()
if ( (&(this->at(length)) - &(this->at(0))) == length * sizeof(TCHAR))) return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
else
{
if (nMinLen < length+1)
nMinLen = length+1;
_tempbuffer = new TCHAR[nMinLen];
_tcscpy(_tempbuffer,this->data());
return _tempbuffer;
}
RelBuf
{
if (NULL != _tempbuffer)
{
*this = _tempbuffer;
delete [] _tempbuffer;
tempbuffer = NULL;
return;
}
this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :sslen(this->c_str())));
}
|
|
|
|

|
I'm afraid this would not be a solution. You can't just return a buffer that you have allocated via operator new. The caller is not expecting to have to free any buffer returned to him/her. So you would end up with a memory leak in this case.
And, while checking addresses might reduce the chance of my "hack" coming back to bite you, it would not eliminate it, I'm afraid. Of course it could only bite you in the mythical implementation that uses non-contiguous buffers but if you actually had such an implementation, there is still the possibility that intermediate characters are not at memory locations between the first and last.
-Joe
|
|
|
|

|
Hi,
Great article. But the class is missing CStringT::Tokenize.
/Mizan
|
|
|
|
|

|
There seems to be an issue with the Trim functions because you use the std::isspace which for Dinkumware in VC6 which doesn't accurately handle international characters properly. I've changed it to use isspace instead but perhaps for somebody else with the same issue something to take into consideration.
Update:
Seems only
bool bRet = std::isspace((WCHAR)L"Ö", std::locale());
has the problem so really only an issue with UNICODE builds.
Karl
modified 4-Mar-12 23:26pm.
|
|
|
|

|
Hey thanks for that. I wasn't aware there was an issue. In the next update I make (which I keep promising but not delivering...) I will put an #ifdef check in there for _MSC_VER that uses regular old isspace for VC6
-Joe
|
|
|
|

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

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

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

|
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!
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
|
|
|
|

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

|
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
I use Code::Blocks with GNU GCC compiler. I just created a new project with nothing else than the stdstring.h and a main.
|
|
|
|

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

|
Solved.
The #define gets rid of the crtdbg problem.
I changed
return vswprintf(pW, pFmtW, vl);
and everything seems to work.
Ten points for your code!
Thks
|
|
|
|

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

|
I must admit, my version is still missing a couple of CString functions that were added in later version. Functions like Tokenize and GetString. I've added them to my local version and hope to do another update here as soon as possible.
-Joe
|
|
|
|

|
When will we see an updated version with these functions added?
Your code sure makes porting code a lot easier, but it would really help to get the last few functions implemented.
|
|
|
|

|
I've been using CStdString for many years now across many projects. I really like how I can write full-features and portable code.
|
|
|
|

|
I tried to build in Visual Studio 2010 Professional and got following unresolve.
1>TestString.obj : error LNK2019: unresolved external symbol "class CStdStr<char> __cdecl operator+(class CStdStr<char> const &,char const *)" (??H@YA?AV?$CStdStr@D@@ABV0@PBD@Z) referenced in function _main
1>.\Debug\TestString.exe : fatal error LNK1120: 1 unresolved externals
|
|
|
|

|
Oh are you trying the the test project download? Sorry but *that* one, I must admit, is truly out of date. I just updated the stdstring.h individual download, not the test project (yet).
I hope to update that tonight but in the meantime, Try replacing the stdstring.h header that is in the test project with the up-to-date version from the individual download
-Joe
|
|
|
|

|
Based on the recent discussions the code has been updated to fix bugs. The datein the submitted code is of 2001. Please update the latest code here or post a URL where it can be downloaded.
|
|
|
|

|
No the date of the latest code is just a couple of days ago
-Joe
|
|
|
|

|
Yes, you are correct. Please accept my apologies.
|
|
|
|

|
Hi, does this work also with gcc? I'm using eclipse in ubuntu and got several (35) compilation errors.
I'm a c++ newbye...
|
|
|
|

|
Hi,
I would bet good money that any compilation errors you are having mention something about the function vsnprintf or vswprintf. The only problem I seem to see these days with the CStdString is with different, non-standard versions of these functions. It seems to depend upon the compiler/C++ distribution. I have a mass of #ifdef-s in there checking various versions but it's still not perfect
Regardless I am pretty sure I can help you resolve them. Just email me directly (my email address is the earthlink one at the top of the header file) and send the text of of all the error messages. If I learn anything of general use, I will fix the header and post it back here.
-Joe
-Joe
|
|
|
|

|
1.
CString str = '"'; //it`s ok
CStdString str = '"'; //error C2440: "initialize": not from the "char" into "CStdStr "
2.
char GetDrive ()
{
char sz [MAX_PATH] = "";
GetModuleFileName (NULL, sz, MAX_PATH);
return sz [0];
}
CString path = CString(GetDrive ()) + ":";//it`s ok
CStdString path = CStdString(GetDrive ()) + ":"; //error C2440: "": not from the "char" into "CStdString"
|
|
|
|

|
This one mystifies me. What you are doing is a pretty basic operation that I do all the time. It certainly doesn't cause any compilation errors for me.
Are you using Visual Studio? If so, what version? If not, what compiler are you using? Can you send me a simple test project that makes this happen because I can't make it happen in my test project.
-Joe
|
|
|
|
|

|
I have that version too and, again, even with that version I cannot reproduce this error. Such code compiles fine for me.
Can you create a very small test project that includes just StdString.h and this code and that will not build, zip it up and email it to me? I promise I will look right at it.
-Joe
|
|
|
|

|
ok ,you email address is ?
|
|
|
|

|
It is at the very top of the StdString.h header file in the comments. Like on line #7. It is an "earthlink" account. I don't want to post it here because anything that crawls the web could find it and send me yet more spam.
-Joe
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
A Drop-In replacement for CString that builds on the Standard C++ Library's basic_string template
| Type | Article |
| Licence | CPOL |
| First Posted | 28 May 2001 |
| Views | 1,030,159 |
| Downloads | 9,200 |
| Bookmarked | 209 times |
|
|