6

I'm still learning c++, so please bear with me. I'm writing a simple wrapper around boost filesystem paths -- I'm having strange issues with returning temporary strings. Here's my simple class (this is not exact, but pretty close):

typedef const char* CString;
typedef std::string String;
typedef boost::filesystem::path Path;

class FileReference {

    public:

        FileReference(const char* path) : mPath(path) {};

        // returns a path
        String  path() const {
            return mPath.string();
        };

        // returns a path a c string
        CString c_str() const {
            return mPath.string().c_str();
        };

    private:

        Path    mPath;

}

With the little test code below:

FileReference file("c:\\test.txt");

OutputDebugString(file.path().c_str()); // returns correctly c:\test.txt
OutputDebugString(file.c_str());        // returns junk (ie îþîþîþîþîþîþîþîþîþîþî.....)

I'm pretty sure this has to deal with temporaries, but I can't figure out why that would be -- shouldn't everything be copying correctly?

ansiart
  • 2,563
  • 2
  • 23
  • 41

3 Answers3

8
CString c_str() const {
        return mPath.string().c_str();
    };

mPath.string() returns a copy of a std::string. That copy is stored in a temporary which will be destroyed at the end of this expression.

.c_str() returns a pointer to memory which will be destroyed when the string is destroyed, i.e. at the end of this expression.

You are returning a pointer to memory which is already destroyed.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • @PermanentGuest - are you sure? I can't find `.c_str()` in the Boost.Filesystem [documentation](http://www.boost.org/doc/libs/1_41_0/libs/filesystem/doc/reference.html#Class-template-basic_path). – Robᵩ Jul 06 '12 at 19:39
  • @Robᵩ : Those docs are nearly three years old. The [_current_ docs](http://www.boost.org/doc/libs/1_50_0/libs/filesystem/doc/reference.html#path-native-format-observers) mention it. ;-] (This was probably added in FileSystem v3.) – ildjarn Jul 06 '12 at 19:44
  • @PermanentGuest I was getting a bug path.c_str(). I was only getting one character (as of boost 1_49) – ansiart Jul 06 '12 at 20:08
4

Looks like mPath.string() returns a string by value. That temporary string object is destructed as soon as FileReference::c_str() returns, so its c_str() becomes invalid. With such a model, it's not possible to create a c_str() function without introducing some kind of class- or static-level variable for the string.

Consider the following alternatives:

//Returns a string by value (not a pointer!)
//Don't call it c_str() - that'd be misleading
String str() const
{             
     return mPath.string();
} 

or

void str(String &s) const
{             
     s = mPath.string();
} 
Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
0

Expressions

OutputDebugString(file.path().c_str())

and

OutputDebugString(file.c_str())

are similar in that they both effectively call c_str() method on a temporary std::string object and attempt to use the result of that call. The first one calls it directly as file.path().c_str() subexpression. The second one does it more implicitly: inside FileReference::c_str() method.

In the first case the temporary std::string object is explicitly created by file.path() call as an immediate part of the entire expression. In accordance with the rules of the language the lifetime of that temporary object extends to the end of the whole expression, which is why the temporary and the result of c_str() call remain valid throughout.

In the second case the temporary std::string object is created inside FileReference::c_str() method. That temporary object is destroyed when this method returns, meaning that FileReference::c_str() returns a pointer to "dead" data. This is the reason for the "junk" in question.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765