|
I suggest to relpace following code
std::basic_string<char>::traits_type::copy(pDst, pSrc, nChars);
to
#if defined(_MSC_VER) && (_MSC_VER >= 1400) //if MSVC8 and later
std::basic_string<char>::traits_type::_Copy_s(pDst, nDst, pSrc, nChars);
#else
std::basic_string<char>::traits_type::copy(pDst, pSrc, nChars);
#endif
...
and
...
std::basic_string<wchar_t>::traits_type::copy(pDst, pSrc, nChars);
to
#if defined(_MSC_VER) && (_MSC_VER >= 1400) //if MSVC8 and later
std::basic_string<wchar_t>::traits_type::_Copy_s(pDst, nDst, pSrc, nChars);
#else
std::basic_string<wchar_t>::traits_type::copy(pDst, pSrc, nChars);
#endif
to avoid warning:
warning C4996: 'std::char_traits<char>::copy' was declared deprecated
c:\program files\microsoft visual studio 8\vc\include\iosfwd(446) : see declaration of 'std::char_traits<char>::copy'
Message: 'You have used a std:: construct that is not safe. See documentation on how to use the Safe Standard C++ Library
|
|
|
|
|
Some of the old code I am trying to adapt to CStdString is being quirky. There was code that took print format specifications like "%%0.%df",5 and turned it into "%0.5f". Other code existed that took the same concept without the variable. Eg CString::Format("%%.0f");
When I try to do the same thing with CStdString, it seems that without any arguments "%%.0f" doesn't turn into "%.0f".
<br />
CStdString s3;<br />
s3.Format("%%.0f-%%.0f/%%d");<br />
TRACE("%s\n",s3.c_str());
CStdString s4;<br />
s4.Format("%%.0f-%%.0f/%%d",1);<br />
TRACE("%s\n",s4.c_str());
Am I doing something wrong? Is Format("%%d") known to not transform to "%d"?
I know there were better ways to do this, and if I wasn't hesitant jump into Boost and STLPort at the same time I would just switch to Boost.Format with pre-made formatters for this bit of code.
|
|
|
|
|
Wow! That's a great catch. It's a bug by me, not doubt. Sorry about that.
It came about because of my SS_SAFE_FORMAT fix. You can read about WHY I put that code in there (search the header file for SS_SAFE_FORMAT and in the comments you'll find a full explanation. but the long and short of it is I tried to "optimize" the case of calling format with no arguments like this:
void Format(const CT* szFmt)
{
Fmt()
*this = szFmt;
}
Unfortunately, this optimization caused the code to totally leave the string alone. I am not sure if the behavior you are looking for is guaranteed by CString::Format() but since CString::Format() does it, I should to.
You have two options to make this work as you want it
1. QUICK FIX
=============
A very quick fix for it is to go into the header file and comment out the line that says this:
#define SS_SAFE_FORMAT
This will make the code work as it should. Unfortunately it will have the unfortunate side effect of making the following code crash your programs (actually as it should but CString lets you get away with it)
CStdString sName("Joe");
CStdString sMsg;
sMsg.Format(_T("My name is %s"), sName); // CRASH!
With SS_SAFE_FORMAT commented out, you would be forced to re-write the last line in of the following ways
sMsg.Format(_T("My name is %s"), sName.GetString());
sMsg.Format(_T("My name is %s"), sName.c_str());
sMsg.Fromat(_T("My name is %s"), static_cast<PCTSTR>(sName));
2. BETTER FIX
================
A better fix is to rewrite the no-argument overload of the Format() function. The version that looks like this:
void Format(const CT* szFmt)
{
Fmt()
*this = szFmt;
}
Should be rewritten to look like this:
void Format(const CT* szFmt)
{
va_list argList;
va_start(argList, szFmt);
FormatV(szFmt, argList);
va_end(argList);
}
Similarly the overload for Format() that looks like this:
void Format(UINT nId)
{
MYTYPE strFmt;
if ( strFmt.Load(nId) )
{
this->swap(strFmt);
}
}
should be rewritten to look like this
void Format(UINT nId)
{
MYTYPE strFmt;
if ( strFmt.Load(nId) )
{
va_list argList;
va_start(argList, szFmt);
FormatV(strFmt, argList);
va_end(argList);
}
}
-Joe
-- modified at 10:29 Monday 26th February, 2007
|
|
|
|
|
Joe
Are you planning to release an updated version? The changes seem simple enough to make but I thought I would ask.
Thanks,
Kevin
|
|
|
|
|
your article / Class is great, helped me a lot with my project, just a simple unicode linux console application... just letting you know that i really appreciate your work.
keep on coding dude... keep on coding
|
|
|
|
|
Glad you like it. Sorry I did not see your post until now. I have just discovered TWO posts on this page that are very old which I have never seen until now.
-Joe
|
|
|
|
|
I am trying to use STLPort as just headers (no io, no lib) in my project because the VC6 std::map iterators don't work across DLL boundaries. I am also trying to remove other MFC C* classes from the same DLL where I switched to std::map.
Since I am not using STLPort's iostreams, I was running into a problem using stringstream. It would pull MFC's sstream and say basic_streambuf is not defined. So I looked for another way to replace all the CStrings and I found CStdString.
CStdString works great until I put the location of /stlport into the additional include directories. When I do that I get many errors in xlocale, xiosbase, xlocmon & etc starting with:
c:\program files\microsoft visual studio\vc98\include\xlocale(20) : error C2011: 'locale' : 'class' type redefinition
c:\program files\microsoft visual studio\vc98\include\xlocale(410) : error C2011: 'ctype_base' : 'class' type redefinition
c:\program files\microsoft visual studio\vc98\include\xlocale(513) : error C2953: 'ctype' : template class has already been defined
c:\program files\microsoft visual studio\vc98\include\xlocale(513) : see declaration of 'ctype'
I've tried all the combinations of settings I can think of in the STLPort config files. I've read the comments in their config files and in StdString.h. I can't find __STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS in STLport-4.6.2, so I don't know if some new macro is the problem.
One more thing I am doing differently. I do not have /stlport in the vc86 directory. It is a sub-directory in my project folder and I have tried absolute and relative paths to it. (If anyone can tell me how to make $(WkspDir) or an equivalent in the "Additional include directories" field of the C/C++ Preprocessor category, I'd appreciate it.)
Thank you,
Jacob
|
|
|
|
|
I gave in and compiled STLPort and stuck it where it wanted to go. I guess I'll carry stlport_vc646.dll around with the project.
I am still interested in knowing how I could have gotten this to work in "headers only" mode.
|
|
|
|
|
Hi Jacob,
I am not sure why it is telling you that locale has already been defined but the error message should tell you WHERE it is defined. Shall I assume that you are picking up both STLPort's locale and Visual C++'s?
This sounds like a configuration problem. However I have not used STLPort for years so I cannot rightly tell you how to get around it. Are there notes in the STLPort distribution that discuss integrating it with Visual C++
Incidentally, since it seems you are using STLPort just because of the std::map iterator issue, can you simply change your DLL's interface to not require passing them across the boundaries. This is not normally a difficult thing to do unless you are dealing with a massive codebase that already does it.
If you have iterators defined in an exported class' definition, you can always try using the PIMPL (Pointer to Implementation) approach. Google it and you'll see what I mean.
-Joe
|
|
|
|
|
The below stated function raises the following error:
StdString.h [line: 3008]
error: cannot convert 'wchar_t*' to 'char*' for argument '1' to
int ssvsprintf (char*, size_t, const char*, char*)
What is wrong here? What must be done to avoid this error?
The ECLIPSE development environment says that function ssvsprintf is
implemented inline by function vsprintf (and only by this function).
Do we not to have to distinguish in this function which type of character
is passed to the function?
void FormatV(const CT* szFormat, va_list argList)
{
#ifdef SS_ANSI
int nLen = sslen(szFormat) + STD_BUF_SIZE;
ssvsprintf(GetBuffer(nLen), nLen-1, szFormat, argList);
ReleaseBuffer();
#else
Furthermore, the usage of function size() is done once without qualifier
"this". This also is reported as error.
Heinz Zechner
|
|
|
|
|
Hi,
First lets be sure you have the very latest version. It is actually not the one on CodeProject (need to fix that) but may be found here:
http://home.earthlink.net/~jmoleary/code/StdString.zip
Please try this and let me know if you still have problems
-Joe
|
|
|
|
|
I am having an issue with that code block too, but mine looks different then his...
void FormatV(const CT* szFormat, va_list argList)
{
#ifdef SS_ANSI
MYTYPE str;
int nLen = sslen(szFormat) + STD_BUF_SIZE;
ssnprintf(str.GetBuffer(nLen), nLen-1, szFormat, argList);
str.ReleaseBuffer();
*this = str;
Mine has ssnprintf, which is being flagged as undefined. His has ssvprintf. I have the latest StdString.h from your web site Joe, so this is the current state.
The question is, should the this function be using ssvprintf, or should the inline defines (under #if defined (__GNUC__) be changed to the ssnprintf?
|
|
|
|
|
YIKES!
My mistake here totally. So sorry. However the fix is simple:
Edit StdString.h header file and replace any instance of the text ssnprintf with ssvsprintf . That should fix your problem.
I fixed this a while ago but I seem to not have that version on my website. So sorry. This should fix things for both you and Heinz
-Joe
|
|
|
|
|
Hi,
I received this error when I tried to use the StdString.h:
e:\datagen\com\cstdstring\stdstring.h(2766) : warning C4786: '?Format@?$CStdStr@D@@QAEXIABVtemplate-parameter-CAB@ABVtemplate-parameter-CAC@ABVtemplate-parameter-CAD@ABVtemplate-parameter-CAE@ABVtemplate-parameter-CAF@ABVtemplate-parameter-CAG@ABVte
mplate-parameter-CAH@ABVtemplate-parameter-CAI@ABVtemplate-parameter-CAJ@@Z' : identifier was truncated to '255' characters in the browser information
e:\datagen\com\cstdstring\stdstring.h(3940) : see reference to class template instantiation 'CStdStr<char>' being compiled
Can you let me know what causes it? I did some research and found out that the file can not be called from the precompiled header. Is it true?
Thanks,
Phil
|
|
|
|
|
Hi Philip,
This is something I used to see frequently with Microsoft Visual Studio. Expanded template parameters which exceed 255 characters are truncated to that length. The warning is harmless. In your case, you are seeing it due to the multiple template parameters of my special "safe" version of Format. More on that later.
If you want to eliminate this warning it you can put this directive in StdString.h
At the top of the file:
#pragma warning (push)<br />
#pragma warning (disable:4786)
And then, at the bottom of the file
#pragma warning (pop)
This will silence it.
You could also turn off the whole "Safe Format" feature to silence it as well. Just make sure the SS_SAFE_FORMAT is not defined in StdString.h or anywhere else in your code. However that option will require you to be careful when calling the Format() function with CStdString arguments. To be specific, you will not be able to do this:
CStdString sMyName(_T("Joe"));
CStdString sSomeVal;
sSomeVal.Format(_T("My name is %s"), sMyName);
...as it will compile just fine but crash your program. Instead you'll have to be very careful to do cast the string argument to the proper expected "C" type, a const TCHAR*. So you'll be forced to do this:
sSomeVal.Format(_T("My name is %s"), sMyName.c_str());
or this:
sSomeVal.Format(_T("My name is %s"), sMyName.GetString());
or this:
sSomeVal.Format(_T("My name is %s"), static_cast<PCTSTR>(sMyName));
This gets annoying because the code compiles either way but that's the danger of using variadic functions like Format() or sprintf().
-Joe
-Joe
|
|
|
|
|
Thanks Joe for your quick response.
I used the first solution and it fixed the problem. Thank you very much.
Regards,
Philip
|
|
|
|
|
Dear Joe,
I have a similar thing unter Visua Studio 2003. Here are the warnings I get:
StdString.h(2120) : warning C4311: 'type cast' : pointer truncation from 'const void *' to 'unsigned long'<br />
StdString.h(2116) : while compiling class-template member function 'bool CStdStr<CT>::TryLoad(const void *)'<br />
with<br />
[<br />
CT=char<br />
]<br />
StdString.h(3940) : see reference to class template instantiation 'CStdStr<CT>' being compiled<br />
with<br />
[<br />
CT=char<br />
]<br />
StdString.h(2122) : warning C4311: 'reinterpret_cast' : pointer truncation from 'const void *' to 'unsigned long'<br />
StdString.h(2125) : warning C4311: 'reinterpret_cast' : pointer truncation from 'const void *' to 'unsigned long'<br />
StdString.h(2120) : warning C4311: 'type cast' : pointer truncation from 'const void *' to 'unsigned long'<br />
StdString.h(2116) : while compiling class-template member function 'bool CStdStr<CT>::TryLoad(const void *)'<br />
with<br />
[<br />
CT=wchar_t<br />
]<br />
StdString.h(3973) : see reference to class template instantiation 'CStdStr<CT>' being compiled<br />
with<br />
[<br />
CT=wchar_t<br />
]<br />
StdString.h(2122) : warning C4311: 'reinterpret_cast' : pointer truncation from 'const void *' to 'unsigned long'<br />
StdString.h(2125) : warning C4311: 'reinterpret_cast' : pointer truncation from 'const void *' to 'unsigned long'<br />
Can I safely ignore them, i.e. #pragma them away?
|
|
|
|
|
Hello,
I have the following behavior and I think its an error, but honestly I'm not sure:
I have some place a little bit place in the memory, say 12 Bytes, and an wchar_t pointer, (wchar_t * ptr;), pointing to the memory. The memory contains an unicode formated string, meaning every other byte is \0. Lets say it contains 'Hello'.
I have an CStdString object named str, and tried the following expression:
str = (wchar_t *)ptr;
After that, str contains the following:
'Hello???????'
So every string is converted the right way, but it is filled up with '?' to the original size in the memory.
Did I something wrong, or is it an error?
Thanks for any help!
Leo
|
|
|
|
|
Hi Leo,
First of all, please be sure you have the very latest version of the code. That is always available at this link:
http://home.earthlink.net/~jmoleary/code/StdString.zip
Assuming you do have it and the problem still occurs, I would have to say you are doing nothing wrong that I can see. The only think I can think is that the string is not NULL terminated. The final two bytes -- after all the characters and their curresponding null bytes -- should be zero. Are they?
If they are and this problem is not going away, could you email me a very simple program or bit of code which illustrates the problem. In particular I'm looking for something that sets up the 12 bytes with the data. I only ask because in my tests, this does not occur.
-Joe O'Leary
-Joe
|
|
|
|
|
Hello Joe,
thank for your quick reply. It seems like I have to do some homework first. I wrote a little program, as you said, and all works fine. So it is obviously not your class. It must be in the context of my project.
Sorry for not doing my testing homework
Thank you!
Leo
|
|
|
|
|
I think you saw this from one of the debug views in Visual Studio. When I look at the contents of a CStdString in MSVS 2003 I see this. The memory however is correct. EG a string "2003" appears to be "2003"??e, but the memory in the buffer _Bx->_Buf is [0x0032, 0x0030, 0x0030, 0x0033, 0x0000, 0x0000, 0x0000, 0x0000] so seems fine... Prob just the debugger definition of std::string being confused with CStdString...
|
|
|
|
|
Hello,
I'm currently experimenting with huge data and get a problem with the Format function:
<br />
CStdString str;<br />
char *pHuge;
<br />
str.Format("%s", pHuge);<br />
str only contains 1024 characters after that call. It seems like a limitation, but I cannot find any parameter to change it.
I thought about writing str = CStdString(pHuge) (it works fine), but how do I replace str.Format("%S", pHuge) then?
I would appreciate any help.
Thanks!
P.S.: Thanks for sharing such a great class!
|
|
|
|
|
Hi Leo,
Well one way to fix this would be to change the values of the parameters that control the operation of the FormatV() function. You see, way back when I wrote this class, I decided I didn't like how slow CString::Format() is and I figured I'd "improve" upon it by having my version allocate its memory on the stack and not calculate the length of the needed buffer each time. Well this is great and it sure works in about 99.9% of cases that people use Format, but in your particular case, not so well.
The approach I used was to loop through, trying a to format using a series of increasing length buffers allocated via the _alloca() function. This is great in that it allocates memory on the stack. It is bad for the same reason. If it needs to allocate a buffer too large it can blow your stack and crash your program. So I created parameters to limit my function to how much stack memory it could allocate to prevent it from blowing the stack. They cover most cases but huge strings
In short, I need to fix this function. While I WILL write something cleaner, below I have included a fix that should work for you (unfortunately this forum removes all indenting so it looks less readable). Look in the StdString.h header for the implementation of the function FormatV. Completely replace the implementation you find there with the one I have below. Note that you will also have to define the parameter called MAX_STACK_FMT_SIZE. In mine I defined it to 2000 -- so never will more than 2000 characters (max 4k in unicode scenarios) be allocated on the stack at any one time. You may limit this to something smaller if you wish. Just put a
#define MAX_STACK_FMT_SIZE 2000
or something like that immediately above the function:
<br />
void FormatV(const CT* szFormat, va_list argList)<br />
{<br />
#ifdef SS_ANSI<br />
MYTYPE str;<br />
int nLen = sslen(szFormat) + STD_BUF_SIZE;<br />
ssvsprintf(str.GetBuffer(nLen), nLen-1, szFormat, argList);<br />
str.ReleaseBuffer();<br />
*this = str;<br />
<br />
#else<br />
<br />
CT* pBuf = NULL;<br />
int nChars = 1;<br />
int nUsed = 0;<br />
size_type nActual = 0;<br />
int nTry = 0;<br />
int nStartLen = sslen(szFormat) + STD_BUF_SIZE;<br />
<br />
do <br />
{<br />
<br />
nChars += nStartLen + (((nTry+1) * FMT_BLOCK_SIZE));<br />
<br />
if (nChars > MAX_STACK_FMT_SIZE)<br />
{<br />
pBuf = new CT[nChars];<br />
nUsed = ssvsprintf(pBuf, nChars-1, szFormat, argList);<br />
<br />
<br />
nActual = nUsed == -1 ? nChars-1 : SSMIN(nUsed, nChars-1);<br />
pBuf[nActual]= '\0';<br />
if ( nUsed > 0 || nTry + 1 >= MAX_FMT_TRIES)<br />
{<br />
this->assign(pBuf, nActual);<br />
}<br />
<br />
delete [] pBuf;<br />
}<br />
else<br />
{<br />
pBuf = reinterpret_cast<CT*>(_alloca(sizeof(CT)*nChars));<br />
nUsed = ssvsprintf(pBuf, nChars-1, szFormat, argList);<br />
<br />
<br />
nActual = nUsed == -1 ? nChars-1 : SSMIN(nUsed, nChars-1);<br />
pBuf[nActual]= '\0';<br />
if ( nUsed > 0 || nTry + 1 >= MAX_FMT_TRIES)<br />
{<br />
this->assign(pBuf, nActual);<br />
}<br />
}<br />
<br />
<br />
} while ( nUsed < 0 && nTry++ < MAX_FMT_TRIES );<br />
<br />
<br />
<br />
#endif<br />
}
Please try this and let me know if it works for you
-Joe
|
|
|
|
|
Let me also add that I hope you are not planning on using Format() to merely assign one string to another this way. I hope you are actually truly formatting characters into it. Because otherwise it would be a huge waste. The assignment operators will work for both CHAR and WCHAR based strings.
-Joe
|
|
|
|
|
Hi Joe,
indeed I only need it for "converting" some space in the memory to a string. So I have a pointer to a string (char or wchar) and want to have a CStdString object representing this string.
I don't know that the assignment works for wchar based strings too, so I thought I can solve it calling the Format function. I tried the assignment solution and it works fine. So I don't need the Format function for this purpose and can use the assignment instead. It is a much better solution I think.
Thank you for the qick and detailed answers. For interest I will try your Format solution as well.
Many thanks
Leo
|
|
|
|
|