views:

242

answers:

3

Hi,

I am stuck in a printf problem. I would appreciate if I can get some help here: In the below code, I can see the font family get displaced correctly in first printf(), but if I set it to variable, i only get an empty string. How can I put it in a variable and have the right values? I just don't want to type 'font.family().family().string().utf8().data()' everywhere?

I did this in the same method:

void myMethod() {
      const char* fontFamily = font.family().family().string().utf8().data();
      // get displayed correctly
      printf ("drawText1 %s \n", font.family().family().string().utf8().data());
      // get an empty string
      printf ("drawText2 %s \n", fontFamily);
}

And the signature of 'data()' is

class CString {
public:
    CString() { }
    CString(const char*);
    CString(const char*, unsigned length);
    CString(CStringBuffer* buffer) : m_buffer(buffer) { }
    static CString newUninitialized(size_t length, char*& characterBuffer);

    const char* data() const;
 //...

}

The signature of utf8() is

class String {
 CString utf8() const;
}

Thank you.

+4  A: 

Something in the chain of font.family().family().string().utf8().data() is returning a temporary object. In your first printf, the temporary object doesn't go out of scope until the printf returns. In the second printf, the temporary has been destroyed after the pointer assignment was made, and the pointer is now invalid. You're seeing a classic example of "undefined behavior".

There are two ways to fix this. Either make a copy of the data before the temporary gets destroyed, or make a reference to the temporary. The copy is probably easiest and clearest, as long as the class has a copy operator. Assuming that utf8() generates a temporary CString, this would be

CString fontFamily = font.family().family().string().utf8();
printf ("drawText2 %s \n", fontFamily.data());
Mark Ransom
This is wrong. Temporaries are guaranteed to hang around for as long as need be (more or less).
anon
A reference to a temporary is guaranteed, but a pointer to some internal data member? I think not.
Mark Ransom
There are no pointers in the quoted code. "Internal" pointers will be taken care of by the c++ constructor/destructor mechanism.
anon
@Neil, the data() function is returning a char *, that is the pointer I am referring to. `utf8()` is probably returning a temporary CString, although that is just a guess.
Mark Ransom
@Mark It doesn't matter. Assuming a CString has a reasonable copy constructor the temporary will remain valid. This is a guarantee made by the C++ language standard.
anon
@Neil Butterworth: What are you talking about? It is perfectly possible that any of the intermediate calls returns a temporary. This temporary will be killed immediately after the initialization of `fontFamily` (this leaving `fontFamily` hanging in the air). There are no guarantees in the language that would extend the lifetime of the temporary. Where did you get that idea? The only similar-sounding thing in C++ is guaranteed for references specifically, but we have no references in this example.
AndreyT
@AndreyT A temporary must live to the end of the expression it is part of.
anon
@Neil Butterworth: Apparently you misunderstood. Mark must be talking about the *second* `printf` - the one that "doesn't work". The expression in this case is used to initialize a `const char* fontFamily` pointer. Once the initialization is over, the temporary is destroyed. And `fontFamily` pointer is left hanging in the air. Yet the OP is trying to print the string it is supposedly pointing to. The result is UB for the *second* `printf`. The first `printf` is indeed OK, becuse the whole thing is one expression.
AndreyT
@AndreyT Mark may have been talking about any number of things - my comment was about what he actually said. He nowhere mentioned the second printf.
anon
@Neil Butterworth: Well, the topic of this question is (as it is clearly expressed by the OP): why the *second* `printf` doesn't work as expected, while the first one does.
AndreyT
@AndreyT and @Neil, thanks for making me see how ambiguous my language was. I'll clean up the answer.
Mark Ransom
A: 

The call to data() (assuming it is called on a std::string) does not necessarily return a null-terminated string. You almost certainly want c_str(), which is defined to do so.

anon
+1  A: 

You are caching a pointer that resides in the temporary returned by utf8() (as Mark and Neil have argued about). You'll have to change fontFamily to either a CString or const CString & to keep the result from utf8() in scope.

MSN
Thanks. This fixes the problem.
michael