Sign in to follow this  
Burnt_Fyr

Weird issue losing characters from std::string

Recommended Posts

I just forked a program I'm working on, and recompiled the edited branch, and somehow am losing characters from a std::string passed as a parameter in a function.

ShipWrightLoader shl;
shl.Load("k800.txt", &sh);

when i get inside the function:

void ShipWright::ShipWrightLoader::Load(std::string filename, ShipHull* sh)
{
// function body
}

the filename is truncated to ".txt"

I've tested with a longer filename, and it seems that the string is being split in 2, leaving just the last half of the characters. I'm suspecting some issue with MBS/unicode settings, but am lost as to where to begin the hunt. I should mention that the untouched branch still works just fine, and the forked branch was created by copying the entire solution, renaming the vc2010 project and solutions, and adding my graphics library that the new branch will use(previously it was based in GDI).

 

EDIT: after more testing it seems even more strange. In the load function, if file loading is unsuccessful a warning is sent to std::cout(redirected to a console window) and the warning prints out the correct filename, using the same parameter string passed into the function.

	// further down the load function
        if(success)
	{
		// snipped
		
	}
	else
	{
		std::cout << "Error opening file:" << filename  << "\n";
	}
Edited by Burnt_Fyr

Share this post


Link to post
Share on other sites

I didn't think the code was relevant, as in the OP the filename appeared to be corrupted being passed IN to the fuction. As well, it is still working AS IS on the unedited fork, so I assume it had something to do with a project setting. Also, it's hacky code that is used just to have a model to work with while i code features in the program. Anyways, I cleaned it up a bit down to the smallest chuck of code that will still display an error for me,and here it is:

void ShipWright::ShipWrightLoader::Load(std::string filename, ShipHull* sh)
{
    std::ifstream file;
    int major,minor,build,revision;
    file.open(filename, std::ifstream::in);
    
    if(file.good())
    {
        char Line[33];
        memset(Line,0,33);


        file.get(Line, 32);

        major = Line[16];
        minor = Line[20];
        build = Line[24];
        revision = Line[28];
    }
    
    else
    {
        std::cout << "Error opening file:" << filename  << "\n";
    }

}

Share this post


Link to post
Share on other sites

How do you determine the content of filename inside the function?

 

If a std::string contains a zero character anything that operates on char* will think it's truncated.

I almost upvoted this, but quickly realized that this is not the issue with the filename "abcgde.txt" which is truncated to "de.txt"

Share this post


Link to post
Share on other sites

Just a quick quess... well I don't think it is really that related to the problem, but do you get a different result if you pass in the string as const reference (which you should probably be doing anyway for performance reasons, though it may not be a difference here since a std::string is getting constructed anyway).

void ShipWright::ShipWrightLoader::Load(const std::string& filename, ShipHull* sh)
{
}

Share this post


Link to post
Share on other sites

After a fresh pot of coffee, and some stepping, i found the issue, but would like clarification on what happened if possible. As i said before, the std::string appeared to be wrong from the outset, but later appears to be unscathed.

 

stepping into the construction of the std::string, i end up here:

//xstring
basic_string(const _Elem *_Ptr)
		: _Mybase()
		{	// construct from [_Ptr, <null>)
		_Tidy();
		assign(_Ptr);
		}

when I hit assign, i noticed this section of code:

	_Myt& assign(const _Elem *_Ptr, size_type _Count)
		{	// assign [_Ptr, _Ptr + _Count)
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Count != 0)
			_DEBUG_POINTER(_Ptr);
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

		if (_Inside(_Ptr))
			return (assign(*this, _Ptr - _Myptr(), _Count));	// substring

		if (_Grow(_Count))
			{	// make room and assign new stuff
			_Traits::copy(_Myptr(), _Ptr, _Count);
			_Eos(_Count);
			}
		return (*this);
		}

My graphics library had iterator debug disabled, which was done in order to improve program speed while working on my old dirtbag computer. When i linked the graphics lib to this exe, i set the exe's iterator debugging to the same level so as to avoid the conflict during linking. Setting both the library project and the exe project to iter debug level 2 seems to have fixed the problem, and I have verified that this works during release mode as well. But I'm stumped as to why. Could someone with more experience shed some light?

Share this post


Link to post
Share on other sites

 

Just a quick quess... well I don't think it is really that related to the problem, but do you get a different result if you pass in the string as const reference (which you should probably be doing anyway for performance reasons, though it may not be a difference here since a std::string is getting constructed anyway).

void ShipWright::ShipWrightLoader::Load(const std::string& filename, ShipHull* sh)
{
}

As the issue was solved before i read this, i did not check. As a rule I strive for const correctness, but only on code that will see the light of day. This was quick code to get something loaded and was put together in about 10 minutes. It worked fine in the other branch, so I hadn't worried about it yet, assuming that I would deal with it during my next refactor. I always try to get something working first, then deconstruct and rebuild using the best practices i can.

Share this post


Link to post
Share on other sites

Anything that is not C, resides in different build units (lib/dll/exe) and is interfacing means trouble if they are not built with the same exact settings.

 

Exact settings in the sense that the objects created in one unit may not safely be passed to a different unit if they do not compile to the same code in both.

 

Esp. classes from templates fall in this category. Even when they are just inside a header, every compilation unit will have it's own copy of the actual code.

Edited by Endurion

Share this post


Link to post
Share on other sites
Did you notice the pattern that the first 4 characters are discarded, regardless of string length? Linking two different versions of the String class caused slicing: the reader thinks the string begins four characters past the location where the writer thinks it begins. It's like reading a book starting from page 5 and not realizing that one is missing a part. Presumably the "debug" string variant adds a 4 byte field, like a pointer or a length, before the string characters; of course, getting far enough to access a wrong-named file instead of segfaulting is a miracle of undefined behaviour. Advice: 1. Kill your old dirtbag compiler: obsolete C++ might be "retro", but incoherent linking has never been acceptable. 2. When in doubt, delete all compiler output and compile everything from scratch.

Share this post


Link to post
Share on other sites

If the string is passed in correctly, and later is printed out with std::cout correctly, how do you know characters are being lost?

My vote is on your debugger showing you the wrong value, but that the string is fine and the file itself is in the wrong location.

 

Try printing out (with std::cout) the value of the string before and after "file.open(filename, std::ifstream::in);".

If they both print out the correct string value, and the only wrong string value is shown in your debugger, then you know it's the debugger that has the bug. Improperly displayed debugger values is a fairly common occurrence for the debugger I use. smile.png

 

You have another unrelated problem here:

major = Line[16];
minor = Line[20];
build = Line[24];
revision = Line[28];

Line[16] is a char; so only 0-255 of the version will be copied into 'major'... Line[17-19] won't be copied over. (Here's a compilable example)

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

If the string is passed in correctly, and later is printed out with std::cout correctly, how do you know characters are being lost?

My vote is on your debugger showing you the wrong value, but that the string is fine and the file itself is in the wrong location.

 

Try printing out (with std::cout) the value of the string before and after "file.open(filename, std::ifstream::in);".

If they both print out the correct string value, and the only wrong string value is shown in your debugger, then you know it's the debugger that has the bug. This is a fairly common occurrence for the debugger I use. smile.png

 

You have another unrelated problem here:

major = Line[16];
minor = Line[20];
build = Line[24];
revision = Line[28];

Line[16] is a char; so only 0-255 of the version will be copied into 'major'... Line[17-19] won't be copied over. (Here's a compilable example)

good catch, thanks. I've been working with flat txt files, and will be switching to binaries with the next minor update, . I'm sure i would have caught when those ints were actually used for something. And you were right about the debugger. somehow even though i copied the project, it's working directory was changed back to default, rather than $(OutDir) in vs2010 express. This was not an issue in the other version, because i had data files in both the projectdir,  and the outdir.

Share this post


Link to post
Share on other sites

Did you notice the pattern that the first 4 characters are discarded, regardless of string length? Linking two different versions of the String class caused slicing: the reader thinks the string begins four characters past the location where the writer thinks it begins. It's like reading a book starting from page 5 and not realizing that one is missing a part. Presumably the "debug" string variant adds a 4 byte field, like a pointer or a length, before the string characters; of course, getting far enough to access a wrong-named file instead of segfaulting is a miracle of undefined behaviour. Advice: 1. Kill your old dirtbag compiler: obsolete C++ might be "retro", but incoherent linking has never been acceptable. 2. When in doubt, delete all compiler output and compile everything from scratch.

It was not the first 4 characters, but half the string. on "k1820304.txt" it would return "04.txt" . in regards to your advice... that dirtbag was my only computer at the time, and 2nd, i always attempt that, as i've seen the voodoo that msvc can manifest. I generally don't ask for help unless i've exhausted all known avenues of attack.

Share this post


Link to post
Share on other sites

Meh, Visual Studio isn't too bad.

 

The reason you get a problem is because you are calling a function in a library that uses a template parameter, and the compilation unit you called it from and the library don't have the same settings. The name mangling of the template resolves to the same thing despite the layout of the classes being different, so the compiler and linker thinks the class is the same size when it isn't.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this