std::string Problems

Started by
15 comments, last by jflanglois 18 years, 2 months ago
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*.
Advertisement
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.
--> The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones <--
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!
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.
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 ;}
--> The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones <--
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. :-)
Argh! That wasn't right either! It's the structure I'm using to hold the data before rendering!

This is gonna be a hard one to solve!
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;        if ( OnScreen(Job) )        {            switch (Job.Type)            {                case XPaintJob::PJ_TEXT:                {                    std::cout << Job.Text << std::endl; // NOPE!                    std::cout << m_vPJobs.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.)
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()?)
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?

This topic is closed to new replies.

Advertisement