convert std::string - LPCSTR and std::string to wchar_t*

Started by
5 comments, last by kiome 16 years, 5 months ago
Well, the subject tells everything I want to do, I've made a functions to convert both a std::string to a LPCSTR, and a function to convert a std::string to a wchar_t*. However, both of them don't work. these are the functions:
LPCSTR shtrIO::ConvertStrToLPCSTR(std::string stringVar)
{
	LPCSTR lpcstrReturn;
	lpcstrReturn = stringVar.c_str();
	return lpcstrReturn;
}

wchar_t* shtrIO::ConvertStrToWChar(std::string stringVar)
{
	const char *tempChar = stringVar.c_str();
	size_t origsize = strlen(tempChar) + 1;
	const size_t newsize = 100;
	size_t convertedChars = 0;
	wchar_t wcstring[newsize];
	mbstowcs_s(&convertedChars, wcstring, origsize, tempChar, _TRUNCATE);
	wcscat_s(wcstring, L"(wchar_t *)");
	return wcstring;
}
I use them as following, first import a main file that defines where all the game files are:
void shtrIO::InputInitialization()
{
	std::string initFileLine;
	std::ifstream initFile;

	initFile.open("./Data/main.txt");

	if (initFile.is_open()) {
		getline (initFile, initFileLine);
		if (initFileLine == "ENDFILE") {
			initFile.close();
			return;
		}
		
		if (initFileLine == "BEGINTERRAIN") {
			getline (initFile, initFileLine);
			while (initFileLine != "ENDTERRAIN") {
				//std::string tempString = initFileLine;
				//wchar_t passedValue;
				//ConvertStrToWChar(tempString, passedValue);
				shtrGameClass.TerrainMembers[numImportedTerrains].TerrainLocation = ConvertStrToWChar(initFileLine);
				getline (initFile, initFileLine);
				numImportedTerrains++;
			}
		}

		getline (initFile, initFileLine);
		
		if (initFileLine == "ENDFILE") {
			initFile.close();
			return;
		}
	} else { 
		MessageBox(0, "Error opening main file", "Error", MB_OK);
		PostQuitMessage(0);
	}
}
Problem 1: Error Message: warning C4172: returning address of local variable or temporary I know what the error message means, but I haven't got an idea on how to fix it. As you can see, the program imports the line "./Data/Terrain/terrain.txt" and stores it in shtrGameClass.TerrainMembers[numImportedTerrains].TerrainLocation (numImportedTerrains is a var I'm going to use in the future, right now, it's allways 0). However the following happens: before storing it, the value of initFileLine = "./Data/Terrain/terrain.txt" after going through the ConvertStrToWChar function, but BEFORE returning wcstring, the wchar_t* = "./Data/Terrain/terrain.txt(wchar_t)" after the function ends, the value in the terrainLocation var = "./Data/Terrain/terrain.txt(wchar_t)" but when I get a new line from the main.txt file (the new line = ENDTERRAIN), the value get scrambled up and goes to: "./Data/Ter(chinees signs)" How do I keep the value "./Data/Terrain/terrain.txt(wchar_t)"? Problem 2: Lets assume the file was opened correctly by the second function, here below (by replacing shtrGameClass.TerrainMembers[0].TerrainLocation with the actual file):
void shtrIO::InputTerrain()
{
	std::ifstream terrainFile;
	std::string terrainFileLine;
	terrainFile.open(shtrGameClass.TerrainMembers[0].TerrainLocation);

	int timesXVarPassed;
	int numTextures = 0;

	if (terrainFile.is_open()) {
		timesXVarPassed = 0;
		getline(terrainFile, terrainFileLine);
		if (terrainFileLine == "BEGINTEXTURES") {
			getline(terrainFile, terrainFileLine);
			while (terrainFileLine != "ENDTEXTURES") {
				shtrGameClass.TerrainMembers[0].textures[numTextures].textureNumber = numTextures;
				shtrGameClass.TerrainMembers[0].textures[numTextures].textureFile = ConvertStrToLPCSTR(terrainFileLine);
				numTextures++;
				getline(terrainFile, terrainFileLine);
			}
			getline(terrainFile, terrainFileLine);
		} else {
			MessageBox(0, "No textures in terrain file", "Error in terrain", MB_OK);
			PostQuitMessage(0);
		}

		if (terrainFileLine == "BEGINDESC") {
			getline(terrainFile, terrainFileLine);
			shtrGameClass.TerrainMembers[0].xSize = ConvertStrToInt(terrainFileLine);
			getline(terrainFile, terrainFileLine);
			shtrGameClass.TerrainMembers[0].ySize = ConvertStrToInt(terrainFileLine);
			getline(terrainFile, terrainFileLine);
			shtrGameClass.TerrainMembers[0].tileXSize = (float)ConvertStrToInt(terrainFileLine);
			getline(terrainFile, terrainFileLine);
			shtrGameClass.TerrainMembers[0].tileYSize = (float)ConvertStrToInt(terrainFileLine);
			getline(terrainFile, terrainFileLine);
			if (terrainFileLine == "ENDDESC") {
				getline(terrainFile, terrainFileLine);
			} else {
				MessageBox(0, "TerrainDesc should have 4 values", "Error in terrain", MB_OK);
				PostQuitMessage(0);
			}
		}


		if (terrainFileLine == "BEGINTERRAIN") {
			while (terrainFileLine != "ENDTERRAIN") {
				for (int xVar = 0; xVar < shtrGameClass.TerrainMembers[0].xSize; xVar++) {
					for (int yVar = 0; yVar < shtrGameClass.TerrainMembers[0].ySize; yVar++) {
						getline(terrainFile, terrainFileLine);
						shtrGameClass.TerrainMembers[0].TerrainLayout[xVar][yVar] = ConvertStrToInt(terrainFileLine);
					}
				}
				timesXVarPassed++;
			}
		} else {
			MessageBox(0, "No terrain body in terrain file", "Error in terrain", MB_OK);
			PostQuitMessage(0);
		}
	} else {
	MessageBox(0, "Error opening terrain file", "Error in terrain", MB_OK);
	}

	shtrGameClass.numTerrainFiles = 1;

	terrainFile.close();
}
It loops through the entire file, correctly storing the xsize, ysize, texture number etc. However, upon trying to store the texture path (LPCSTR for ease of use in the CreateTexture2D function), the following happens: before passing terrainFileLine to the ConvertStrToLPCSTR function, the value is: "./Data/Textures/terrain1.dds" Before returning lpcstrReturn to the texturefile var, lpcstrReturn = "./Data/Textures/terrain1.dds" After 'closing' the function, the lpcstrReturn value suddenly turns to: "îþîþîþîþîþî" (repeating) This value also gets stored in the textureFile variable. I think I gave all the information needed, could somebody help me out here?
Advertisement
wchar_t* shtrIO::ConvertStrToWChar(std::string stringVar){	const char *tempChar = stringVar.c_str();	size_t origsize = strlen(tempChar) + 1;	const size_t newsize = 100;	size_t convertedChars = 0;	wchar_t wcstring[newsize];	mbstowcs_s(&convertedChars, wcstring, origsize, tempChar, _TRUNCATE);	wcscat_s(wcstring, L"(wchar_t *)");	return wcstring;}


See wcstring? As a local array, its address is meaningless once the function is returned. You need to dynamically allocate the string data, using either new[] (bad) or use something like std::wstring. This also solves your problem of possible under-allocation when using an arbitrary upper limit like 100 characters.

I believe that is the cause of problem 2, parts of the stack are overwritten, including the place where your temporary array live. However, the result of returning a pointer to a local are undefined, so technically anything could happen.

Also note that there is no need to use strlen, std::string::size will tell you how long the string is.

I imagine you could change your loading to use std::vector or some other dynamic container rather than relying on what appear to be an array and a max size.
that means that I'll have to convert the std::wstring to a wchat_t later on... How do we do that (Yes, I've been googeling ;))?
Quote:Original post by MadMax1992
that means that I'll have to convert the std::wstring to a wchat_t later on... How do we do that (Yes, I've been googeling ;))?


std::string is actually a typedef of std::basic_string<char>. std::wstring is a typedef of std::basic_string<wchar_t>. As they share the same underlying implementation (char traits aside), you can use std::wstring::c_str() to get a wchar_t pointer.
Regarding the first point about std::string to LPCSTR, it is not a good idea to store the return from std::string::c_str() for later use.

std::string::c_str() behaves as though a temporary null-terminated C-style string is created from the std::string and returned and is not guaranteed to be valid after subsequent operations on the owning std::string. It is absolutley guaranteed not to be valid once the owning string is destroyed.

If you really, really needed to store the result, you will need to allocate a new array and copy the return from c_str() into it.

However, for the vast majority of situations, it would be preferable to just call std::string::c_str() on the string whenever you needed the array.
I know I'm sounding a little bit stupid, I've been busy for quite a while with the function, trying to make it work. And somehow, I've been unable to fix my problems. Could somebody give me an example of how you would fix problem #1?

Thanks in advance!
If your main problem is converting between wide-char and multi-byte strings on Windows, this little tutorial might help.
Though, don't ever fully trust any online tutorial, even if it works it might cause problems later at some point (I wrote that tutorial, that's another reason to not fully trust it ;).
My Blog

This topic is closed to new replies.

Advertisement