|
This is not a bug. You just need to be sure to call c_str() on the CStdString object whenever you supply it as one of the "..." arguments in a variadic function like fprintf, sprintf, CStdString::Format, etc.
It's true that MFCs CString allows you to get away with just this:
fprintf(fp, "%s\n", str);
But that is a hack and is not something you should rely upon. Even Microsoft recommends you cast CString when supplying it as one of the "..." arguments in a function like fprintf:
fprintf(fp, "%s\n", (const char*)str);
...or you could do this:
fprintf(fp, "%s\n", static_cast<const char*>(str));.
.and with CStdString, you can do this as well.
fprintf(fp, "%s\n", str.c_str());
All three examples above have the exact same effect -- turning the CStdString object into a const char*. A const char* is what fprintf needs to supply to the %s, not a CStdString.
Whatever you do, don't call GetBuffer(). That's for getting NON-const access to the string buffer. You don't need a writeable buffer string buffer. You need a read-only one, a const char*
If you read the other feedback to the CStdString article you'll see this question asked many times. You might need to adjust the date filter to see all the feedback. But once again, this is not a bug. It is an annoyance caused by variadic functions (i.e. functions that take a variable number of arguments) and it is something I cannot possibly design around. Read the other feedback for a more thorough explanation.
|
|
|
|
|
Thanks!
|
|
|
|
|
|
First off, thank you for this class. It's been extremely helpful. I'm compiling for the first time on UNIX (SunOS 5.8) and getting several errors (e.g., DWORD not defined) all within the "ssfmtmsg" function below. Based on the header comment, I'm guessing the #else may be out of place, but then I'd be surprised it wasn't caught sooner. Please let me know if I'm doing something wrong. Thanks for your help.
// -----------------------------------------------------------------------------
// ssfmtmsg: FormatMessage equivalents. Needed because I added a CString facade
// Again -- no equivalent of these on non-Win32 builds but their might one day
// be one if the message facet gets implemented
// -----------------------------------------------------------------------------
#if defined (SS_WIN32) && !defined(SS_ANSI)
#else
inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
DWORD dwLangId, PSTR pBuf, DWORD nSize,
va_list* vlArgs)
{
return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
pBuf, nSize,vlArgs);
}
inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
DWORD dwLangId, PWSTR pBuf, DWORD nSize,
va_list* vlArgs)
{
return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
pBuf, nSize,vlArgs);
}
#endif
Bill
|
|
|
|
|
Well a DWORD is an unsigned long, but I think that's not the problem here. The problem is that I appear to have misplaced my preprocessor macros. I put the
#else
line in the wrong place. It should be BELOW those ssfmtmsg functions, not above them. If you move it downward to the correct spot, you should be OK.
FormatMessageA and FormatMessageA only exist on Win32 platforms.
I will correct the online version tonight. Sorry about that.
-Joe
|
|
|
|
|
we develop windows program with vc(but not use MFC).
Our program complied ,and display follow warning:
/***************************************************/
warning C4786: 'std::_Tree<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<c
har> > const ,CMarkupSTL::SavedPos>,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,CMarkupSTL::SavedPos,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<cmarkupstl::savedp
os> >::_Kfn,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<cmarkupstl::savedpos> >' : identifier was truncated to '255' characters in the debug information
/***************************************************/
CMarkupSTL is the class that encapsulates the dom object.
As using the template, the symbol lenght may be less than 255.
Display the warning,why?
|
|
|
|
|
It is a warning very common with Visual C++ and the Standard C++ Library. What it is telling you is that the fully decorated C++ name of a class (instantiated from a template) was longer than 255 characters. That's a problem with templates and VC.
It is harmless but annoying.
If you wish to suppress this warning, you may use a #pragma as follows:
#pragma warning (disable:4786)
Place this pragma as early in the header as you can. I put all such #pragmas in my stdafx.h
Please note however that some VC headers actually turn ON warnings you might have disabled with this statement. In particular the header <yvals.h> does this. Therefore you might need to do something like this
#pragma warning (disable:4786) // disable it
#include <yvals.h> // this will re-enable it
#pragma warning (disable:4786) // disable it AGAIN
Bizarre but it works.
Again, don't worry about the warning. It is completely harmless.
-Joe
|
|
|
|
|
I'm pleasantly suprised that you reply soon.
I'm worring about the worning.
Thank you very much!
|
|
|
|
|
I'm pleasantly suprised that you reply soon.
I'm worring about the worning.
Thank you very much!
|
|
|
|
|
I'm working on a large (personal) project comprising of several DLLs and EXEs. I want to use this string class as a common string type for all projects so i can pass this to the DLLs exported functions. I understand if i use this string header in each separate project it might cause problems because it uses the stl and what if one of the DLLs uses a different stl library to the lib file? what will happen then?
So i wanted to put the implementation of this string class inside a lib file, so the stl implementation is common (at least in the lib) and then i can just include the header (which is not the class definition only) in the projects and also link to the lib file. Is this possible?
Thank you!
|
|
|
|
|
Well, if you have control over all modules which will be passing these objects back and forth (and I'm guessing you must) then as long as you set all of them to use the same DLL version of the CRT, everything should be just fine.
Think about why it would be dangerous for one module to use a static version and another to use the DLL version. Consider the following function, exposed by one of your DLLs:
CStdString Foo();
You have a situation where one module (the DLL) allocates memory and another (the app or whatever other module calls into the DLL) frees it. If they are not using the same heap, bad things happen.
When that returned CStdString object is destroyed by the app, it will be on a different CRT heap than the one that your DLL allocated it from. This will cause an exception.
Just set all modules to use the shared DLL version of the CRT and these problems go away. All modules then share the same heap.
Alternately, you can change the interface to NOT use CStdString objects at all, but rather to fill buffers supplied by the caller. For example, here is the above function rewritten to use this principle:
void Foo(char* buf, int maxSize);
It is far more cumbersome, but it removes any memory allocation issues sinc the memory is now allocated AND freed by the caller only.
Incidentally, you would have the EXACT same problem if you were merely using the STL's std::string. My class does derive from that one, but it adds only functions to the base class, no member data.
My recommendation -- do not try to export CStdString from a DLL or LIB at all. That is what I do. I just keep the header file in some common directory and rely upon the vendor-supplied basic_string<> implementations to be exported properly. The functions CStdString adds are mainly small, inline wrappers on existing basic_string<> functions already. The compiler should resolve most of them away to nothing anyway
If you are going to have DLL-exported functions which return string objects, definitely set all modules to use the DLL version of the CRT. But you do not need any special handling for CStdString. Allow its functions to be compiled into every module if necessary. The extra cost in module size will not be great. Most modules use only a small fraction of the member functions anyway
-Joe
|
|
|
|
|
OK Thanks
While i will initially be writing all the DLLs and EXE for the project there may eventually be other developers writing the DLLs (its a plugn architecture). Even if a programmer uses a different STL version to compile their DLL then this should in theory be ok?
Thanks
Andrew
|
|
|
|
|
If some other developer comes along and uses a different STL and tries to plug in and use your DLL function that returns a CStdString, then yes, you will have a problem. You would have exactly the same problem if the return value was a std::string or std::wstring -- different heaps.
In this case, the way around it is to change the interface of the plug-in functions to NOT use std::string or CStdString objects. For example, instead of this:
Function exported by the DLL
CStdStringA Foo()
{
return CStdStringA("this is a return value");
}
Main application:
void main(int)
{
CStdStringA value = Foo();
// do something with value
}
You could instead do something like this this (note - no error checking):
Function exported by DLL
char* Foo(char* buf, int maxSize)
{
return strncpy(buf, "this is a return value", maxSize);
}
Main application
void main(int)
{
char buf[100];
CStdStringA value(Foo(buf, 99));
// do something with buf
}
A lot more cumbersome I admit, but now there is no problem with heaps. That's the problem with plug-in architectures, I'm afraid. It's the same reason why COM uses the BSTR string type with all those dedicated functions to allocate and free them -- they need to ensure that the same heap is used.
-Joe
|
|
|
|
|
Ok, thanks for your quick and helpful responses. I have one more query (at least until I read your response ).
Lets say i have a function exported by my dll :
void GetDescription(CStdString *desc)
{
*desc = "This is the descrption";
}
Then in the exe :
int main()
{
CStdString *a, b;
a = new CStdString;
GetDescription(a);
GetDescription(b);
return 0;
}
The CStdString is declared in the exe and it passes a pointer to the GetDescription function. Now, once inside the dll the string pointers, which currently have a size internally of 0, will require some memory allocation to store the string data. Where does this get allocated? Will it break when main() exits and tries to destroy the string? Does it make a difference that a is on the heap and be is on the stack?
Thank you again
Andrew
|
|
|
|
|
You need to understand that there are TWO allocations taking place for each string you pass into this function. One allocation is of the string object itself, the other is of the internal buffer managed by the string object. To illustrate, here is a simplified class description for CStdString (imagine that CStdString was just a simple class not derived from anything with a bunch of operators & ctors omitted):
class CStdString
{
const char* buffer;
int length;
CStdString() : buffer(0), length(0) {}
~CStdString() { if ( 0 != buffer ) free(buffer); }
}
Now in the case of the string objects themselves, the EXE has total control over when they are allocated and freed. The stack one ('b') goes away at the end of main(). The heap one ('a') goes away whenever you call delete (I'm assuming you forgot to in your example).
But in the case of the internal buffers managed by the string objects, you do NOT always have control over when that memory is allocted and freed. All a module has to do is try to assign a value to your string object (the way that GetDescription does) and deep down inside the CRT, the malloc() function will likely be called. But since malloc is being called from GetDescription(), inside the DLL, it uses that DLLs heap. Not the EXEs. If the DLL and the EXE use different heaps... you are in trouble.
void main(int)
{
CStdString b; // object allocated, buffer is not
GetDescription(&b); // buffer allocated by the DLL's heap
} //--> b is destroyed. Its dtor frees the buffer but, since
// we're back in the EXE, it tries to free it from EXE's heap
-Joe
-Joe
|
|
|
|
|
I understand all that (I think from when i first posted to the forum to now the DLL / EXE heap penny has dropped!). Yes i forgot the delete call from my example.
In the example you just listed, I'm presuming when main exited bad things would happen as it tried to free something from the DLL's heap?
Will this happen even if both projects use the Multithreaded DLL runtime libs?
I thought I'd try answer this for myself, i set up a simple exe and dll project, in the exe i had this code:
int main(int)
{
Str str;
DoFoo(str);
return 0;
}
where DoFoo is a DLL exported function. The dll code looks like this:
extern "C" __declspec( dllexport ) void DoFoo(Str &str)
{
str.alloc(10);
}
the str class looks something like this:
class Str
{
char *str;
public:
Str()
{
str = NULL;
}
~Str()
{
delete str;
}
void alloc(int val)
{
str = new char[val];
}
};
This seems to work fine with the CRT types set to Multithreaded dll (it asserts with anything else). The desctructor gets called and it seems there are no memory leaks. I included the following to test for leaks:
#include <crtdbg.h>
#define _CRTDBG_MAP_ALLOC
So is this code all working as it seems? Does the memory get deleted properly etc? Or is it tricking me?
How would this work if i replaced Str with CStdString? Is it a whole different kettle of fish with the STL?
Thanks!
|
|
|
|
|
Andrew Newton wrote:
In the example you just listed, I'm presuming when main exited bad things would happen as it tried to free something from the DLL's heap?
Will this happen even if both projects use the Multithreaded DLL runtime libs?
No there will be no problem if both projects (i.e. modules) use exactly (and I do mean EXACTLY) the same DLL version of the runtime libs.
When you publish a module with functions like GetDescription (which modifies string objects) what you are doing is imposing a requirement on any other module that uses it (be it an EXE, another DLL, etc) that that module MUST use exactly the same DLL version of the CRT.
Even if you have 100 DLLs loaded, if they were all built with the same DLL version of the CRT, they all use the same heap so there are no problems.
This issue is the same whether you use Str, basic_string<>, or CStdString. Underneath they're all calling malloc() and free() eventually.
It is for reasons like this that all the Win32 functions use that more-difficult-to-use semantics like this
char* GetDescription(char* buf, int, maxSize);
This eliminates any heap issues altogether. In this were how you defined GetDescription, it wouldn't matter what version of the CRT you or the caller used. But it's no fun to code against...
-Joe
|
|
|
|
|
Excellent, thanks for all your help. I understand fully now.
I did at one time consider using syntax something like the following..
void GetDescription(char* buf, int maxSize);
...as it would prevent issues like the ones we discussed but decided to go with a much more friendly form of string classes. Now i won't have any hesitation to include your string lib in my code.
Your help is much appreciated
Andrew
|
|
|
|
|
This string class rocks. The only issue that I have is that when using the .NET debugger, I can't see the values of any CStdString variables... the watch windows in .NET just display garbage. To see the contents of the string, I have to TRACE() them in the code, or use OutputDebugString(). This is a hassle for sure.
Any reason why this would occur. Is this more to do with STL's basic_string over CStdString?
Thx!
Greg
|
|
|
|
|
Just an addition... it seems that this only occurs if the string is longer than 15 (?) characters. If it's less, it seems to work fine.
|
|
|
|
|
If you take a look into the implementation of basic_string, you'll see that PJ Plauger was a little bit tricky in this implementation.
Basically inside the string is a union. One member of the union is a character buffer (about 16 characters, I think). The other member is a character pointer.
When you assign string value to a basic_string<> object, the code checks the length. If it is shorter than 16 characters (as most strings are) then it just uses the buffer member of the union. In this way it avoids having to do a costly heap allocation. If the value is longer than 16 characters, it allocates memory from the heap and puts the address in the string-pointer member of the union. In this case, the rest of the buffer might (and as you have seen, DOES) hold garbage.
The VS.Net debugger is not able, I think, to distinguish at run-time what to do so it just shows you the buffer every time. But if you expand the string object in the watch window and look at that pointer member of the union, you should see the string.
Since CStdString derives from basic_string<> it has the same data members and the same problem.
Try the very same code with std::string or std::wstring and you should see the same garbage when the string is longer than 16 characters.
Now I know that with VC6, there was a file named autoexp.dat that you could edit to tell the debugger how to handle certain unknown types. It always treated my CStdString objects as basic_string<>s and had the same problem. Unfortunately I could never find a way to put any intelligence into it to make it do anything special based on string length. Perhaps VS.Net has some new way to do this?
If not, I'm afraid you'll have to manually expand the contents of the string object in the watch window to see the value.
-Joe
|
|
|
|
|
|
I am having trouble compiling StdString.h file on LINUX platform. Has anyone done it? What do I need to do?
I am using GNU auto-config method.
../include/StdString.h:1373:67: macro "isspace" passed 2 arguments, but takes just 1
John W Willcox III
|
|
|
|
|
My best guess is that your version of the Standard C++ Library is out of date, I think. "isspace" is not supposed to be a macro. There is a global "C" function by that name which takes one argument (the character in question) and a Standard C++ function in the 'std' namespace which takes two arguments (the character in question and a locale object). There should not be a macro by that name.
If you are using Gnu you need to have the G++ library version 3.0 or later, I believe. From there on people seem to have few problems using CStdString. Which version are you using?
Also, make sure you have the very latest version of the code. This isn't going to solve your problem, though.:
http://home.earthlink.net/~jmoleary/code/StdString.zip
-Joe
|
|
|
|
|
I use gcc version 2.96. According to the website:
http://www.delorie.com/gnu/docs/libg++/g++FAQ_54.html (Copied below)
I should be okay. I am confused?
////////////////////////////////////////////////////////////////////////////////
Does g++ support the Standard Template Library?
If you want to use the Standard Template Library, do not pass go,
upgrade immediately to gcc-2.8.0 or to egcs. The new C++ front end
handles STL very well, and the high-quality implementation of STL from
SGI is included verbatim as part of the libstdc++ class library.
If for some reason you must use 2.7.2, you can probably get by with the
hacked-up version of the old implementation from HP that is included
with libg++-2.7.2, but it is definitely inferior and has more problems.
Alternatively, g++ 2.7.2.x users might try the following: a group at the
Moscow Center for Sparc Technology has a port of the SGI STL
implementation that mostly works with gcc-2.7.2. See
`http://www.ipmce.su/people/fbp/stl/stlport.html'.
////////////////////////////////////////////////////////////////////////////////
John W Willcox III
|
|
|
|
|