|
The problem is exactly what you identified in the title of your post. You are using a CStdString object as a POD type when it is not one.
Whenever you pass a CStdString as one of the "variadic" parameters to a function like fprintf (i.e. one of the "..." parameters), you must be sure to properly cast it to the proper POD type first. Otherwise, the compiler will simply take the first 4 bytes of the object in binary layout.
So this line is bad:
_ftprintf_s(file, _T("%s%s"), str, buf);
but this line is OK
_ftprintf_s(file, _T("%s%s"), str.c_str(), buf);
Other variations that would be OK include the following:
_ftprintf_s(file, _T("%s%s"), str.GetString(), buf);
_ftprintf_s(file, _T("%s%s"), static_cast<PCTSTR>(str), buf);
Yes, I realize that MFC's CString lets you get away without doing this, but they specifically laid out the class in binary to allow it. In short, it's a hack they put in there to protect you from yourself.
Since I derive CStdString from whatever implementation of basic_string your library provides, I have no control over the binary layout of the class.
-Joe
|
|
|
|
|
Joe,
yes, I knew the .c_str() works, but I thought removing it would be ok using the extension posted by balint256 in this thread. And so asked him if this usage was one of his points of extension.
CStdString str(_T("world!"));
_tprintf(_T("Hello %s\n"), str);
// Output is "Hello world!"
Otherwise, I don't see what is the point of the extension.
|
|
|
|
|
Sorry, I somehow missed that this was a response to his post.
I must admit I haven't looked at his scheme until now. Now that I have, I would not recommend it. He adds to the size of the class, introduces multiple inheritance and must update a pointer after every single function call, yet he fails to update that pointer in all places needed. All to avoid a simple call to c_str() in rarely called functions.
The only reason I ever created my CStdString::Format "hack" in the first place, was because it improves the safety of the class, and in no way reduces it. Unfortunately, balint's fix does not do that. I do not mean to criticize balint. He clearly wanted to help. But I cannot recommend this fix as it is incomplete and decreases the safety of the code.
Also, my fix was purely for those converting legacy code. Not for new code. If you are writing new code, do yourself a favor: Every time you call a variadic function with a CStdString arguments, call the c_str() function on the argument. It's what I do.
-Joe
modified on Tuesday, June 16, 2009 9:49 AM
|
|
|
|
|
Got it. Thanks for the clarification, otherwise, I would have to waste time to figure out why balint's extension not working.
I will go back to the original one.
BTW, I sent you an email asking for the newest source with project if possible. Did you get it?
|
|
|
|
|
Oh that was an email? I put that aside thinking, "I'll get to this later today". Then when I tried to, I kept looking for a post about it here and couldn't see it.
Will go back, find it, and send you the latest.
-Joe
|
|
|
|
|
Hi everyone.
Can't get the demo project to work
Fehler 1 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""class CStdStr<char> __cdecl operator+(class CStdStr<char> const &,char const *)" (??H@YA?AV?$CStdStr@D@@ABV0@PBD@Z)" in Funktion "_main". TestString.obj
Any idea?
|
|
|
|
|
Hi,
It has been ages since I updated that project file. I think I generated it with Visual Studio 6 or earlier. Unfortunately, the VS Upgrade wizard doesn't seem to properly upgrade it for me either. So I went and created a new one. You can do it yourself if you want or you can get it from me. Doing it is pretty simple. Just
1. create a new console project.
2. Add the 2 string resources that were in the old project(IDS_STRING1 and IDS_STRING2) and put whatever values in them you want.
3. Move the code from TestString.cpp in the old file to the new file. Don't forget the #include statements.
You might, however want to get the latest version of the header file. You can email me and I'll send it to you, along with the updated project file, if you prefer.
-Joe
|
|
|
|
|
Hi,
I am using CStdString in a WTL project and I had some issues because when using atltime.h I get :
>C:\Program Files\Microsoft Visual Studio 9.0\VC\ce\atlmfc\include\atltime.h(281) : error C2039: 'Preallocate' : is not a member of 'CStdStr<ct>'
This error also occurs with the WTL::CString and it seems Preallocate it's only declared in
ATL::CString version in atlsimpstr.h :
void Preallocate( _In_ int nLength )
{
PrepareWrite( nLength );
}
PXSTR PrepareWrite( _In_ int nLength )
{
CStringData* pOldData = GetData();
int nShared = 1-pOldData->nRefs;
int nTooShort = pOldData->nAllocLength-nLength;
if( (nShared|nTooShort) < 0 )
{
PrepareWrite2( nLength );
}
return( m_pszData );
}
How can we make the same things with CStdString ?
|
|
|
|
|
Hi,
Over the years, as Microsoft has updated CString, it has been difficult keeping CStdString up to date with it. Preallocate is certainly a function added in recent versions, I guess when they changed the hierarchy to have CSimpleStringT and CStringT
At first glance it looks like it just ensures the buffer is the requested size. I still have to determine the rules (i.e. should the buffer be shrunk if it is larger? Is existing data preserved? etc) but it should not be too hard to implement.
A crude implementation might be something like this
void Preallocate(int nLength)
{
if (this->GetLength() < nLength)
{
this->resize(nLength);
}
}
Note that this example assumes a string that's already longer than the requested length would not need to be truncated.
I'll have to look into this a bit closer though.
-Joe
|
|
|
|
|
Hi Vincent,
To use CStdString a ATL::CSimpleString (not WTL::CString), in your stdafx.h:
#define __ATLSIMPSTR_H__
#include <atlbase.h>
#include "StdString.h"
#define Preallocate(size) reserve(size)
namespace ATL
{
typedef ::CStdString CSimpleString;
};
#define __ATLSTR_H__
cheers,
AR
|
|
|
|
|
Hi,
Does someone know how to solve this :
CStdString strFolder;
m_treeFolder.GetItemText(hItem, strFolder);
BOOL WTL::CTreeViewCtrlT<tbase>::GetItemText(HTREEITEM,BSTR &) const' : cannot convert parameter 2 from 'CStdString' to 'BSTR &'
and GetItemText has 3 overloads :
BOOL GetItemText(HTREEITEM hItem, LPTSTR lpstrText, int nLen) const;
BOOL GetItemText(HTREEITEM hItem, BSTR& bstrText) const;
BOOL GetItemText(HTREEITEM hItem, _CSTRING_NS::CString& strText) const;
I would like to use the third one with CString reference, is it possible to
make CStdString compatible with CString& ? Even simpler I would like to write :
CString strFullPath;
CStdString& str = strFullPath;
|
|
|
|
|
Hi Vincent,
In order to do this, you would need the source code to whatever class m_TreeFolder is. That class only knows about plain-old-datatypes (PODs), BSTRs, and CStrings. It does not know about my class and apparently it does not know about the standard C++ Library template std::basic_string from which my class derives.
What class is m_TreeFolder? Is it a standard MFC class? If so, you are out of luck. However if it is something DERIVED from an MFC class and you have the source code for it, you CAN change that. Let's assume it is a class that you have the source code for. The simplest way to change it would be to add a member function like this:
BOOL GetItemText(HTREEITEM hItem, CStdString& strText) const
{
CString cs;
BOOL bRet = GetItemText(hItem, cs);
if (bRet)
{
strText = cs;
}
return bRet;
}
If m_TreeFolder is not a class you can edit, you might consider deriving a class from it just to do something like the above.
However, even if you cannot edit the class of m_TreeFolder, what is currently there, you could still call one of the existing functions using a CStdString like this:
m_TreeFolder.GetItemText(hItem, strFolder.GetBuffer(100), 100);
strFolder.ReleaseBuffer();
However that is, in my opinion an ugly and unreliable way to do things. I am not a fan of the whole GetBuffer()/ReleaseBuffer() functionality in CString.
Another way would be simply to write a global function:
(Note I am assuming that m_TreeFolder is of a class called "CTreeFolder")
BOOL GetItemText(const CTreeFolder& tf, HTREEITEM hItem, CStdString& cs)
{
CString csTemp;
BOOL bRet = tf.GetItemText(hItem, csTemp)
if (bRet)
{
cs = csTemp;
}
return bRet;
}
I hope this helps,
-Joe O'Leary
-Joe
|
|
|
|
|
|
I am using this class because I am making a program to run on multiple platforms. I made some changes for the windows side like making MAX_FMT_TRIES 10 and FMT_BLOCK_SIZE 100 * 1024.
On the Mac side, it doesn't use that version of format, and instead uses one with a fixed buffer size of 1024 + format string length. It has no try and grow mechanism. You are limited to roughly 1k sized strings.
I am going to try hacking in some kind of grow logic for it.
I see that the class has not been updated in a few years. If your done with it, I guess those things will not be fixed and Ill continue to use my special hacked version.
I also added two new methods which I think are very useful for string classes:
LoadTextFile( ... )
SaveTextFile( ... )
And added templates from the std namespace:
typedef std::vector<cstdstring> CStdStringVec;
typedef std::vector<cstdstring>::iterator; CStdStringVecIter;
and a set using std::list<> as well.
I do not know if you plan on doing any more updates, but those would be nice to have features.
Anyway, this class is a life saver for multi-platform programming.
|
|
|
|
|
You are right. I have to say I really should redo that functionality. It would be simple enough. Mostly my goal was to avoid the insanely complicated CString version which goes through the string and calculates how big of a buffer it needs. That seemed awfully wasteful.
I think the best approach is to try a stack buffer or two and if those do not work, then go to a loop which uses heap memory. That seems to be the best of both worlds. On the Mac side it would be simple enough and still get the advantage of speed in most situations.
I will fix that when I get back from vacation
-Joe O'Leary
-Joe
|
|
|
|
|
See the following for one implementation. glibc 2.1 makes this easy, but win32 requires the power of 2 implementation.
http://xbmc.org/trac/changeset/17674/branches/linuxport/XBMC/guilib/StdString.h
Cheers,
Jonathan
|
|
|
|
|
Hi!
I've using your faboluos CStdString from years.
But now, one problem has arrised: Trim
I use VStudio 6.
CStdString token = "dummy";
token = token.Trim();
Then, "token" is filled with garbage, or "".
This only happens when compiling as type "DLL Release". In "DLL Debug", "EXE Release", "EXE Debug" there are NO problems.
The code is huge (the DLL is about 6 MBytes).
Any idea?
Thx!
PD: I've traced the code until:
MYTYPE& TrimLeft()
{
this->erase(this->begin(),
std::find_if(this->begin(), this->end(), NotSpace<ct>()));
return *this;
}
"this" is correct, but the result of "this->erase" is wrong.
modified on Monday, September 15, 2008 9:28 AM
|
|
|
|
|
I don't know why I am only hearing about your post now, even though you posted it months ago. Sorry, I had no idea there was another question:
I suppose this probably must not be a problem for you anymore but in case it is, I would first say be sure you have the very latest version which you can get here:
http://home.earthlink.net/~jmoleary/code/StdString.zip
But even in the version you have, this should not be happening.
-Joe
-Joe
|
|
|
|
|
Thanks for the great class. I had to make a few changes to get it compiling under g++
Line 1619:
From:
#elif defined(__sgi)
To:
#elif defined(__sgi) || defined(__GNUC__)
Line 2325:
From:
if ( static_cast<int>(size()) < nMinLen )
To:
if ( static_cast<int>(this->size()) < nMinLen )
Line 3255:
From:
return Mid(nFirst, this->GetLength()-nFirst));
To:
return Mid(nFirst, this->GetLength()-nFirst);
Hope this helps someone
|
|
|
|
|
I've also managed to compile StdString with Linux and gcc 4.3.2,there was a warnings about unused variables,
but I've fixed them with adding "(void)" before the variable like so:
(void)nCount;
Thank you!
|
|
|
|
|
Thank you. This helped with my cross-platform development.
John
|
|
|
|
|
Hi Joe, first off, many thanks for StdString.h. I've been using it in a project for several years. I recently got round to checking for any updates, and downloaded the latest from this article (2004-APR-22) I had been using the 2003-JUL-10 version. This one fails to compile on line 3255:
return Mid(nFirst, this->GetLength()-nFirst));
because there is an extra ) on the end.
I'm guessing there is a more recent version somewhere? But I can't find it.
Thanks, Ian
|
|
|
|
|
Hi Ian,
Sorry to take so long (months!) to answer your post. I don't get back here much and my spam filter seems to throw away emails from CodeProject.
There should be a link at the top of the code to a more recent version. I think it still works. If not, I will update things the week of Nov 9-16 when I get back from vacation
-Joe
|
|
|
|
|
The link at the top of the code doesn't work at the moment ("site under construction"). Is there somewhere else I can get the most recent version?
I just remeoved the extra parenthasis in Mid to get it to compile and it works great. I'll definately be making use when trying to port a program to Linux so it would be nice to have the most recent version.
Thanks a lot.
|
|
|
|
|
Hi,
Try this link:
http://home.earthlink.net/~jmoleary/code/StdString.zip
-Joe
|
|
|
|
|