Getting char* array from c++ stringstream ostringstream

13,338

Solution 1

The short answer is you need to use something like strdup to fix this:

const char* makeFilePath(const char *path, const char *name, const char *ext = ".txt")
{
  ostringstream bld;
  bld << path << name << ext;
  cout << "makeFilePath(), returning: \n\t" << bld.str()<< endl;

  return strdup(bld.str().c_str());
}

This is a really sub-optimal solution as now you have a memory leak unless you properly free the result of this function, plus it might return NULL on occasion which could cause chaos if you don't test for it. It'd be much better to return std::string.

If you're using C++, use C++.

Solution 2

stringstream.str() returns a expiring value a temporary string object whose lifetime is restricted to the end of the expression. A pointer to a temporary expiring value when deleted would simply dangle and accessing it beyond the expression is an Undefined behaviour possibly a crash or some garbage value.

One option would be to fetch the string object to a persistent string and then get the constant pointer to the character array buffer.

const std::string bld= stringstream.str();
const char* result= bld.c_str();

Alternatively you may also consider to bind the expiring rvalue to a constant reference (Note some compilers might be lenient to bind to a reference which is possibly incorrect) and then fetch the pointer to the buffer. This will simply extend the lifetime of the temporary object.

But that would not resolve all you pain. As I have missed your function implementation

const char * makeFilePath(const char *path, const char *name, const char *ext =
        ".txt") {
    ostringstream bld;
    bld << path << name << ext;
    cout << "makeFilePath(), returning: \n\t" << bld.str()<< endl;
    string temp = bld.str();
    return temp.c_str();

}

You are returning a pointer to a local object the scope of which is not extended beyond the function scope. A better suggestion would be to return a string object from your function and then on the caller scope convert the string object to a const C null terminated string.

std::string makeFilePath(const char *path, const char *name, const char *ext =
        ".txt") {
    ostringstream bld;
    bld << path << name << ext;
    cout << "makeFilePath(), returning: \n\t" << bld.str()<< endl;
    return bld.str();

}

Alternatively you may want to duplicate the string using strdup, but the caller should be aware to release the resource for the allocated buffer.

Solution 3

Just don't use char *. If you're writing C++, you should use C++ strings, not C strings.

C doesn't have anything to copy strings for you, (except maybe sprintf with the NULL-target GNU extension?), so you are often going to be left with invalid pointers.

using std::ostringstream;
using std::string;

string add(const string fileName) {
    ostringstream bld;
    string prefix = "prefix";
    string suffix = "suffix";
    bld << prefix << " " << fileName << " " << suffix;
    string temp = bld.str();
    cout << "in add(): \n\t" << temp << endl;
    return temp;
}

string makeFilePath(string path, string name, string ext =
        ".txt") {
    ostringstream bld;
    bld << path << name << ext;
    cout << "makeFilePath(), returning: \n\t" << bld << endl;
    string temp = bld.str();
    return temp;

}

int main(int argc, char **argv) { // unfortunately, argv much be char**
    cout << "=== PROJECT START ===" << endl;

    string filePath = "\\Path\\";
    string fileName = "FILENAME";
    string fullFilePath = makeFilePath(filePath, fileName);

    cout << fullFilePath before calling add():\n\t" << fullFilePath << endl;    
    string str = add(fullFilePath);
    cout << fullFilePath after calling add():\n\t" << fullFilePath << endl;

    return 1;
}

There's a broader lesson here, in modern C++ you can delete almost all use of pointers (not just char pointers)

Share:
13,338
JavaFan
Author by

JavaFan

Updated on June 14, 2022

Comments

  • JavaFan
    JavaFan almost 2 years

    I am trying to copy ostringstream to a char* array and am hoping someone can help me with understanding where my mistake lies. I looked on the forum, found some things that are similar and unfortunately am still am unable to get property copy from ostringstream to char*. In short I am trying to copy into the char* via:

    ostringstream bld
    bld<<"test"<<"is"<<"good"
    const char * result = bld.str().c_str();
    

    The complete code to reproduce an error is below. In this code I am having two functions that essentially build strings via ostringstream. In the makeFilePath() function I build a complete file path (ie /path/file.txt). In the add() function, I simply add two more char* arrays to an argument to get prefix /path/file.txt suffix.

    The problem is for some unknown reason to me the fullFilePath also changes to look like prefix /path/file.txt suffix. Last three lines of a code will exhibit that.

    I spent hours on this, thinking maybe this is a referencing issue or something else. However, none that I attemped worked. Any ideas how to get over this problem?

    Thanks!!

    #include <iostream>
    #include <sstream>
    #include <string>
    
    using std::cout;
    using std::endl;
    using std::string;
    
    using std::ostringstream;
    
    const char* add(const char *fileName) {
        ostringstream bld;
        const char *prefix = "prefix";
        const char *suffix = "suffix";
        bld << prefix << " " << fileName << " " << suffix;
        string temp = bld.str();
        cout << "in add(): \n\t" << temp << endl;
        return temp.c_str();
    }
    
    const char * makeFilePath(const char *path, const char *name, const char *ext =
            ".txt") {
        ostringstream bld;
        bld << path << name << ext;
        cout << "makeFilePath(), returning: \n\t" << bld.str()<< endl;
        string temp = bld.str();
        return temp.c_str();
    
    }
    
    int main(int argc, char **argv) {
        cout << "=== PROJECT START ===" << endl;
    
        const char * filePath = "\\Path\\";
        const char *fileName = "FILENAME";
        const char *fullFilePath = makeFilePath(filePath, fileName);
    
        cout << fullFilePath before calling add():\n\t" << fullFilePath << endl;    
        const char* str = add(fullFilePath);
        cout << fullFilePath after calling add():\n\t" << fullFilePath << endl;
    
        return 1;
    }