Sign in to follow this  

std::string Problems

This topic is 4340 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I had some odd problems with strings before, but I'm even more confused now that I've moved to MSVC++ Express Edition. I have two overloaded methods for drawing text called Paint(). They both take the same parameters except that where one accepts a char* the other accepts an std::string. I thought the c_str() method of std::string returned a const char* of the string it contained. To make the overloaded function work with std::string types, I essentially duplicated the code and where I normally inserted the char*, I just called the c_str() method of the std::string. The Paint() method works fine with char* types, but when I pass in an std::string that I assigned "Hello!", the output is something like "$y0". What's going on? Thanks for any help. By the way, using the data() method of std::string yielded the same results when casting the return value to char*.

Share this post


Link to post
Share on other sites
Have you considered the string wasn't constructed properly ?.

I'd suggest keeping only one version of Print (), you may try commenting the Print (char const*) function then pass a C string to the other Print (std::string const&). There is an acceptable conversion from a C-string to std::basic_string, you'll know that the problem comes from your Print () function or the passed string itself:

Print (std::string const&)
{
//....
}


Print ("AABB") ;

The data () method does't return a valid C-string by the way.

Share this post


Link to post
Share on other sites
I'll try commenting the Paint(char*) method out to see what happens.

I just got it to work though. I orignally declared and assigned a string to my std::string object inside of the game loop, which recreated it over and over again! Once I moved it outside (before) the game loop, it worked. However, if I try to use the overloaded + operator within the Paint() function call (something like Paint(TheString + "More text!"), I get the same problem: some odd flickering characters.

Maybe the string isn't being constructed properly like you said. Anyway, it'd be nice if I didn't need the Paing(char*) version of the method. I'll see what I can do. Thanks!

Share this post


Link to post
Share on other sites
This is weird. I didn't even notice the change I made, but passing std::string objects into the Paint() method only works when I pass by reference. If I rewrite the Paint() method to pass the std::string argument by value, I get those same odd characters again!

Here's the working prototype.
void XLib::XGraphics::XDevice::Paint( long iX, long iY, XFont& Font, std::string& tText, int iDepth = 0 );
Notice that the string argument is a reference. I don't understand why this works but passing by value doesn't.

The overloaded version looks like this.
void XLib::XGraphics::XDevice::Paint( long iX, long iY, XFont& Font, char* szText, iDepth = 0 );
Any ideas about why this could be? I'm just trying to get a char* out of the string object.

If it would help, I can post some code.

Share this post


Link to post
Share on other sites
I haven't seen any problems with your posted functions, passing an object by value or by reference (aside from the standard idiom) doesn't break the behavior of the called functions. You may try this little sample:


#include <string>

void doit (std::string const& s)
{
MessageBox (0, s.c_str (), 0, 0) ;
}

void doit (char const* p)
{
MessageBox (0, p, 0, 0) ;
}

void doitnow (std::string &s)
{
MessageBox (0, s.c_str (), 0, 0) ;
}

int main ()
{
doit ("Hello!.") ; //Call the second, the parameter is deduced
//to char const* first

std::basic_string<char> s ("Hi!.") ;
doit (s) ; //Call the first

doit (s + "???") ; //Call the first


std::basic_string<char> ss ("???") ;
doitnow (ss) ; //Legal, call the third

doitnow ("Hello!.") ; //Illegal, compile-time error, you must pass it
//as a const reference (otherwise pass it as a
//value), with respect to the standard.

return 0 ;
}


Share this post


Link to post
Share on other sites
Aha!

The string objects are behaving as they should. To render the text I'm using SDL_ttf, and it seems that the functions I'm passing the final string into are generating the problem. I outputed the string values to the system console several times as they moved through the program and they all worked perfectly.

Now to see if I can understand why TTF_RenderText_...() isn't cooperating.

Thanks for all of your help, Skeleton_V@T. If anyone has any more ideas I'd greatly appreciate them. :-)

Share this post


Link to post
Share on other sites
It seems that the problem I'm having has to do with scope. How can I fix this?

When I pass in a string by reference to Paint() everything works fine, but when I pass in a string by value I get squat! I output the text to the console from all the different variables that hold the string data inside of the Paint() method and viola! They all print out just fine. When I output the same fields to the console just before everything is drawn (within another method after Paint() has been called) they're all blank! I'm thinking the char* returned from c_str() somehow goes out of scope after leaving the Paint() method because I'm making a copy of the string I pass into it.

Here's an example. (I shortened the definitions with elipses.)

void XLib::XGraphics::XDevice::Paint(long iX, long iY, XFont& Font, std::string tText, int iDepth = 0)
{
//...
XPaintJob Job = {
//...
tText.c_str()
//...
};
//...
m_vPJobs.push_back(Job);
//...
std::cout << Job.Text << std::endl; // WORKS!
std::cout << m_vPJobs[m_vPJobs.size() - 1].Text << std::endl; // WORKS!
std::cout << tText << std::endl; // WORKS!
std::cout << tText.c_str() << std::endl; // WORKS!
}
void XLib::XGraphics::XDevice::Render()
{
//...
static XPaintJob Job;
for (int i = 0; i < m_vPJobs.size(); i++)
{
Job = m_vPJobs[i];
if ( OnScreen(Job) )
{
switch (Job.Type)
{
case XPaintJob::PJ_TEXT:
{
std::cout << Job.Text << std::endl; // NOPE!
std::cout << m_vPJobs[i].Text << std::endl; // NOTTA!
//...
}
//...
}
//...
}
}
//...
}

If I build this in debug mode from VC++, I get a few odd characters as output in the Render() method. If I run the application outside of VC++, they're simply blank.

Is this a scope issue? If so, how can I solve it. I can't pass strings by reference into the Paint() method because the compiler can't automatically cast char* types that way! (I'd always have to pass string objects into the Paint() method and not char*'s.)

Share this post


Link to post
Share on other sites
First of all... I must correct your foreign loan-words. "voila" (French); "nada" (Spanish). :)

When you pass the string by value to Paint(), a copy is made which goes out of scope at the end of the function. Then the .c_str() returns a pointer to the internal storage of that local copy. So within the function, all of that works OK.

When the function goes out of scope, the pointer that you saved in the XPaintJob now points at garbage, because the string copy is gone.

When you pass by reference, the pointer points at the buffer within the caller's string instance (because no copy is made), and as long as that string persists until the Render() call, you are ok.

But, while that solves your problem, you have a design issue in that you should always try to "wrap/unwrap" stuff as "tightly" as possible (i.e. leaving it unwrapped only when needed). In particular, if XPaintJob is a struct/class that you get to define, you would be much better off to hold a string member in it, and only do the .c_str() at the time that you make the TTF rendering call.

If that's not possible, then you might want to copy the string text to a new buffer when you insert it into the XPaintJob - but then you have the messy problem of determining how to make sure the memory is freed properly.

(Also, why is 'Job' static in Render()?)

Share this post


Link to post
Share on other sites
Thanks for the language correction. :-)

This is exactly what I thought was happening. There's no reason I can't have the XPaintJob structure hold a string object instead of a const char*, so I'll give that a try.

I tried copying the const char* from c_str() to a new buffer, but that got a bit complicated. I hope that isn't what I have to do in the end!

Let me see if I can get around this by having my XPaintJob structures hold a string.

Oh, and I declared Job as static in the Render() method so that it won't have to be recreated in memory each time the Render() method is called (which happens once every frame/cycle). Is this wrong?

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
First of all... I must correct your foreign loan-words. "voila" (French);
And I must further correct you: voilà.

Skeleton_V@T: Why are you instanciating your strings as std::basic_string< char > (I'm aware that std::string is a typedef for that)?


jfl.

Share this post


Link to post
Share on other sites
Quote:
Original post by jflanglois
Skeleton_V@T: Why are you instanciating your strings as std::basic_string< char > (I'm aware that std::string is a typedef for that)?
jfl.


This is not much of an issue, it is just my habit of building the code for multiple charsets-friendly. I usually use the TCHAR macro instead of the plain char or wchar_t for strings:

std::basic_string<TCHAR>

Share this post


Link to post
Share on other sites
Quote:
Original post by Skeleton_V@T
Quote:
Original post by jflanglois
Skeleton_V@T: Why are you instanciating your strings as std::basic_string< char > (I'm aware that std::string is a typedef for that)?
jfl.


This is not much of an issue, it is just my habit of building the code for multiple charsets-friendly. I usually use the TCHAR macro instead of the plain char or wchar_t for strings:

std::basic_string<TCHAR>


It just seemed like more typing for little reason. Why don't you typedef std::basic_string< TCHAR > tstring;?

jfl.

Share this post


Link to post
Share on other sites
Quote:
Original post by jflanglois
Quote:
Original post by Zahlman
First of all... I must correct your foreign loan-words. "voila" (French);
And I must further correct you: voilà.


You are of course correct... wish I could remember the alt-code for 'à' :) (Typing this stuff on Macs is so much nicer...) Anyway, that one particularly annoys me because my dad *plays* viola ;)

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
Quote:
Original post by jflanglois
Quote:
Original post by Zahlman
First of all... I must correct your foreign loan-words. "voila" (French);
And I must further correct you: voilà.


You are of course correct... wish I could remember the alt-code for 'à' :) (Typing this stuff on Macs is so much nicer...) Anyway, that one particularly annoys me because my dad *plays* viola ;)
Heh. How is it on Mac? The alt-code is 133.
All I know is that I can't stand AZERTY keyboards (which have keys for accented letters).


Quote:
I did. But in my posts sometimes I want to refer them as std::basic_string. Don't ask me, I don't know.
Fair enough :)


jfl.

Share this post


Link to post
Share on other sites

This topic is 4340 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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