Skip to main content
gamedev.net
🔒 Locked

Strings

Started by ofireps Jan 25, 2008 at 2:45 PM 8 replies 3.8k views
Original Post
ofireps
ofireps
Hi, I'm trying to convert my application (it's beginning) from chars and char*s to std::strings. I'm trying, but I can't convert this one into strings:

	TCHAR appPath[MAX_PATH];
	DWORD len = GetModuleFileName(NULL, appPath, MAX_PATH);

	TCHAR drive[_MAX_DRIVE], path[_MAX_DIR];
	_splitpath(appPath, drive, path, NULL, NULL);

	TCHAR logPath[_MAX_PATH];
	_makepath(logPath, drive, path, "files\\file1.x", NULL);
This is what I have right now, but it doesn't really work.. It doesn't even compile..

	std::string Path;
	std::stringstream Stream;

	TCHAR appPath[MAX_PATH];
	DWORD len = GetModuleFileName(NULL, appPath, MAX_PATH);

	TCHAR drive[_MAX_DRIVE], path[_MAX_DIR];
	_splitpath(appPath, drive, path, NULL, NULL);

	Stream << drive << "\\" << path << "\\files\\file1.x";
	Path = Stream.str();

	// TCHAR logPath[_MAX_PATH];
	// _makepath(logPath, drive, path, "files\\file1.x", NULL);
Any suggestions? Thanks, Ofir.
Evil Steve
Evil Steve
In what way does it not compile? What error do you get?

If you're using TCHARs you should avoid explicitly using std::string and std::stringstream, and typedef a version that uses std::wstring and std::wstringstream or std::string and std::stringstream depending on if UNICODE is defined. Or explicitly call the multi byte versions of those functions by postfixing A to the name, e.g. GetModuleFileNameA().
ofireps
ofireps
Unicode is not defined. The compiler says:
error C2079: 'Stream' uses undefined class 'std::basic_stringstream<_Elem,_Traits,_Alloc>'error C2297: '<<' : illegal, right operand has type 'TCHAR [3]' - a few timeserror C2228: left of '.str' must have class/struct/union


Also, I didn't understand how I should do what you explaind..


Thanks again.
Evil Steve
Evil Steve
Quote:
Original post by ofireps
Unicode is not defined. The compiler says:
error C2079: 'Stream' uses undefined class 'std::basic_stringstream<_Elem,_Traits,_Alloc>'error C2297: '<<' : illegal, right operand has type 'TCHAR [3]' - a few timeserror C2228: left of '.str' must have class/struct/union
Did you #include ?

Quote:
Original post by ofireps
Also, I didn't understand how I should do what you explaind..

Thanks again.
Something like this in a common header:
#ifdef UNICODE   typedef std::wstring          tstring;   typedef std::wstringstream    tstringstream;#else   typedef std::string           tstring;   typedef std::stringstream     tstringstream;#endif
And then use tstring and tstringstream in your code. That way, when UNICODE is defined, and TCHAR becomes wchar_t, your code will use the wide char string and stringstream, and it'll cope with multi-byte strings too.

Do you know how TCHAR works to handle unicode?
ofireps
ofireps
Quote:
Original post by Evil Steve
Do you know how TCHAR works to handle unicode?


No. Could you explain?

Evil Steve
Evil Steve
Quote:
Original post by ofireps
Quote:
Original post by Evil Steve
Do you know how TCHAR works to handle unicode?


No. Could you explain?
Sure.

There's two types of character, and therefore two types of C-style strings. char typically means a single ASCII character, which is usually 1 byte in size and signed (Although it doesn't have to be). There's also wchar_t, which is typically 2 bytes and also signed (And again, doesn't have to be). wchar_t is more useful for representing some extended characters that can't fit into a signed char (which has a value from -128 to 127).

If you include various windows headers, you'll get the TCHAR type defined. It's just a typedef for either char or wchar_t, and there's similar "T" versions like LPCTSTR (Long Pointer to a Constant T-STRing - I.e. const TCHAR*).
If the UNICODE preprocessor symbol is defined, then the headers will end up typedef'ing TCHAR as a wchar_t, otherwise they'll typedef it as char.

Because most Win32 functions are in DLLs, they need to be able to cope with both char and wchar_t variants of strings. To do that, they do more preprocessor shennanigans, and actually export two functions; one for char strings and one for wchar_t strings. The actual functions are usually the same as the name you'd expect, with A or W postfixed to the name.
For example, if you right click on the GetModuleFileName function, and select "Go To Definition" in Visual Studio, you'll get taken to this chunk of code in winbase.h:
#ifdef UNICODE#define GetModuleFileName  GetModuleFileNameW#else#define GetModuleFileName  GetModuleFileNameA#endif // !UNICODE

As you can see, it's effectively renaming the function according to if UNICODE is defined.
That lets you explicitly call the narrow or wide character version of the function by calling GetModuleFileNameA or GetModuleFileNameW if you want to (Which I'd recommend for library code that you expect to use in other projects where you're not 100% sure if they'll be unicode or not). This is also why you sometimes can't have a function called E.g. CreateFile without headaches; if the windows header is included before the compiler sees the CreateFile, it'll replace it with CreateFileA or CreateFileW. Which is fine unless you include a windows header in some source files and not others...

Anyway, back on topic - The compiler will automatically define the UNICODE preprocessor symbol if your character set is set to unicode (Project settings -> Configuration Properties -> General -> Character Set in VC2005, presumably in a similar place in other Visual Studio versions), which will cause all functions to call the unicode versions of the functions.

However, the STL doesn't use the UNICODE macro, a std::string is always a string of chars, and a std::wstring is always a string of wchar_t's. That means if you call the c_str() member of a std::string and pass the return value to a Win32 function, it'll work in a non-unicode build, but give a compile error in a unicode build because the function will expect a wchar_t* instead.
You can get around that by explicitly calling the unicode or non-unicode versions of the function depending on if you're using a std::string or std::wstring, but that begins to get ugly fast. Using the tstring method I showed above, you can get the correct type of string passed to the Win32 function.

Hmm, this post was a little longer and more drivelling than I expected [smile]
ofireps
ofireps
Wow. Thanks, it really helped me. :)

Now I have another problem: I can't clear the stringstream object..
I tried using Stream.clear() but it didn't help... How should I do it correctly?

Thanks,
Ofir.
Zakwayda
Zakwayda
Quote:
Now I have another problem: I can't clear the stringstream object..
I tried using Stream.clear() but it didn't help... How should I do it correctly?
The documentation should tell you what you need to know.

However, I would suggest creating a new stream object rather than trying to re-use an old one.
ofireps
ofireps
Thanks a lot! I finally did it using Stream.str(""); to set it to "".

Thanks again!!
Ofir.
Zahlman
Zahlman
Quote:
Original post by jyk
However, I would suggest creating a new stream object rather than trying to re-use an old one.


Seconded. Sometimes just emptying the buffer isn't enough, and anyway, re-creating the stream often leads to clearer code. Especially if you are doing something in a loop: just put the stream construction inside the loop - now it's re-created each time automatically, and the variable is properly scoped, for free.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.