|
I use your test project and modify teststring.cpp as follows:
typedef std::map<cstdstring, int,="" stdstringlessnocase=""> CMyMap;
CMyMap myMap;
myMap[_T("MYKEY")] = 7;
_ASSERTE(myMap.find(_T("mykey")) != myMap.end());
/////your code//////////
typedef stdext::hash_map<cstdstring,int> CMyHash;
CMyHash myhash;
myhash[_T("ABC")]=12;
////my code//////
and got:
error C2440: 'type cast' : cannot convert from 'const CStdString' to 'size_t' c:\vs8\vc\include\xhash 61
regards.
|
|
|
|
|
Hi,
I'm afraid I can't tell what the problem is because you need to edit your post. The declaration of your map type did not come out because the angle bracket characters are hidden. In order to make them show up, you need to use the ones from the "Formatting" bar below the editor when you are writing your post
Can you repost again showing me exactly how your map was declared, being careful to use the '<' and '>' characters from the "Formatting" section?
-Joe
|
|
|
|
|
I'm using a library from a vendor that depends on CStdString, but I'm having linking problems, so I created a new project that just includes StdString.h. Here's the entire source code:
#include "StdString.h"
int _tmain(int argc, _TCHAR* argv[])<br />
{<br />
CStdString test;<br />
return 0;<br />
}
The problem comes about during the linking state. I'm getting the follow errors:
1>st.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __thiscall CStdStr<wchar_t>::~CStdStr<wchar_t>(void)" (__imp_??1?$CStdStr@_W@@QAE@XZ) referenced in function _wmain<br />
1>st.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __thiscall CStdStr<wchar_t>::CStdStr<wchar_t>(void)" (__imp_??0?$CStdStr@_W@@QAE@XZ) referenced in function _wmain<br />
1>C:\Documents and Settings\user\My Documents\Visual Studio 2005\Projects\st\Debug\st.exe : fatal error LNK1120: 2 unresolved externals
It has some issue with the inline class members such that the linker can't find them, even though they should be in the object for st.obj. I'd appreciate any help, as it's pretty critical that we get this code running.
Thanks,
Kenny
|
|
|
|
|
First make sure you have the latest version of the code. You can get it here:
http://home.earthlink.net/~jmoleary/code/StdString.zip
Next, make sure that your application is linking in a compliant version of the Standard C++ Library. Check your project settings to be sure. As an experiment, try to use the base class and see if you also have problems. For example:
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
std::string test;
return 0;
}
Try this. Does it compile and link? If not, you have a configuration problem
-Joe
|
|
|
|
|
Joe,
Thanks for the quick reply. Trying std::string test worked fine. I downloaded the newest version, and though there were some compiler warnings, it seemed to link fine. I haven't tested it past linking yet.
Assuming it works fine, I am concerned about whether I'll be able to use it. My application is using functions from a DLL that was built against an older version of StdString.h (the one that I was originally trying). Will I see problems if I'm including the newest version in my project, but the DLL was built against an older version?
Thanks,
Kenny
|
|
|
|
|
Hmmm. It depends upon how old the DLLs.
Really CStdString is just a bunch of inline functions added on to the basic_string class. It adds no member data. So older versions of the class should work just fine with newer versions. It is irrelevant if, the current version of, say, CStdString::Format() is different from an older one because neither is acting upon any member data except through public members of basic_string itself.
There should be no binary differences because there is no "binary" whatsoever except that of the base_class basic_string.
Technically, I suppose it depends upon how old the version of CStdString is that you are linking against. In some VERY old versions (we're talking like around the year 2001 or earlier) it was not a template at all but rather a class. It had a single, static member. But those days are long gone.
Is the DLL you are using exporting functions which refer to CStdString by name? In other words, do the functions you call either take or return CStdString objects? If so, then that DLL is probably trying to export the CStdString as well. Check out the section in the StdString.h header file entitled "HOW TO EXPORT CSTDSTRING FROM A DLL". It might have some relevant comments. I wrote those comments when working with a version of Visual C++ two generations ago, however so I can't be sure how much of it still applies..
I still think you will be fine but lacking the actual code, it is difficult to be sure. Regardless
-Joe
|
|
|
|
|
Joe,
I figured things out based on the mangled names. The definition of CStdString in the DLL is CStdStringA, whereas I my code was using CStdStringW.
Thanks for the help and the code
-Kenny
|
|
|
|
|
Joe,
A message of yours on 20 Apr 06 says your latest version of the code uses:
typedef std::codecvt SSCodeCvt;
However the code I downloaded today (8/26/07) uses the following:
typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
Which is correct? I haven't tried it lately, but my original problem with Borland was on this line.
- Dave -
|
|
|
|
|
Hi Dave,
The second line is correct. Actually what you see in the second line is what I typed in the message you are referring to. But I forgot that this board interprets angle bracket characters (<,> )specially. I just typed them literally and the board promptly hid them and everything in between them. In order to make them appear you have to use those special "formatting" buttons at the bottom of the textbox when you are composing the message.
Anyway, I have gone back and edited that message to make it show up properly. Sorry for the confusion.
-Joe
|
|
|
|
|
I'm a relative beginner at C++ right now, so please bear with me if this is obvious.
Say I have a function "void RunTest(CStdString)". If I pass a MFC CString as a parameter, I get the compiler error "Cannot convert parameter 1 from 'CString' to 'CStdString'". Is there a way to pass an MFC CString into the function without having to do any explicit casts?
For example, I would just like the following code to compile and work (using the function above):
CString s;
RunTest(s);
The reason I ask is that I would like to use CStdString in some libraries, but there is already a lot of code that uses CStrings. Having to modify that code would be very tedious (and I simply don't want to mess the code up).
Also, going the other direction, I get a compiler error when I pass a CStdString in for a function expecting a CString parameter. For example, if I have a function "void RunTest2(CString)" and run the code:
CStdString stdstring;
RunTest2(stdstring);
... I get the error "Cannot convert parameter 1 from 'CStdString' to 'CString'."
It seems like this class is supposed to be able to handle these two problems directly, so I don't know if I have a compiler setting wrong or something (I'm compiling on VS .Net 2003). Is there something I'm missing here? (Again, sorry if this is an elementary question.)
|
|
|
|
|
The problem here is that you are attempting to perform two implicit conversions. C++ will only perform one for you
CString can be implicitly converted to a const TCHAR*
A const TCHAR* can be implicitly converted to a CStdString
C++ will do either of those for you automatically. However what it will NOT do is both of them.
Personally, when I am writing a function that takes a string argument which I know I am not going to change during the course of the function, I write the argument as a const TCHAR*, not as a CStdString. If I need CStdString functions, I just construct an object around the const TCHAR*.
So instead of this:
void RunTest(CStdString s)
write this
void RunTest(const TCHAR* szVal)
or this (same thing)
void RunTest(PCTSTR szVal)<br />
{<br />
<br />
CStdString s(szVal);<br />
}
Now you can call this function with just a CString object. The C++ compiler will see that the function takes a const TCHAR* and will implicitly call the CString class' conversion function for you (operator ()).
Hope this helps
-Joe
|
|
|
|
|
Hi,
Im an embedded C++ developer and my compiler is Diab and this doesnot support MFC's CString. I came across your article and felt so excited to use the portable CStdString instead of the CString. So, I just inserted your CStdString.h and tried to compile. Unfortunately, the compiler gave a whole bunch of errors saying it doesnot recognize locale.h and when I tried to include locale.h, it complains missing of another header file and when I finally included all the dependent header files (crtdefs.h,vadefs,h) of locale.h, the diab finally gave me an error saying:Only Win32 target supported.
The functions that I am interested to port are Empty() and Format() in CString of which CString is MFC dependent and my platform doesnot support it. When I tried to include your CStdString as is, the compiler gave me many errors (other than the missing header files) of which I was not able to find a solution for them. Is there a chance where you can strip off part of the code in your header file and just make the Empty() and Format() functions work in my embedded diab compiler??
Really appreciate your help. Im desperately waiting for your response.
|
|
|
|
|
OK, I don't know what is going on I have been back here many times and responded to posts but I have NEVER seen this post until right now, even though you made it in MAY. I can miss things but not that many times. Grrrrr...
Anyway I feel terrible that I never gave you a response here. At this point I imagine you have moved on but if not, please let me know. Regardless please accept my apologies for not responding to you sooner but I swear I never saw this post.
-Joe
|
|
|
|
|
I have a program. There are some CString::Format function call, and each Format's argument count are all over 17B
But in your CStdString, the safe Format can only accept at most 17 paremetes.
I don't want to undefine SS_SAFE_FORMAT, what I can do?
|
|
|
|
|
Hi,
I have to say I am surprised that any program ever called Format with more than 17 arguments. I figured that was a pretty safe number.
You could create the overloads yourself. What is the maximum number of arguments passed in? Creating overloads is just matter of copy, paste and edit.
Barring that, you have no choice but to undefine SS_SAFE_FORMAT
My main recommendation would be to not use Format at all but rather to use stringstreams. With stringstreams all the formatting is safe and there is no limit on the number of arguments. Frankly Format() and functions like it that take a variable number of arguments are not a good thing to use anyway. Streams are much better.
-Joe
|
|
|
|
|
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
|
|
|
|
|