C++ toString() with sprintf()

2.2k Views Asked by At

With C++ I am trying to implement a toString() function for my class:

void ClassName::toString(string& returnString)
{
    sprintf(returnString.c_str(), "Position: (%f, %f, %f)\n", position.x, position.y, position.y);
}

However I keep getting this error: argument of type const char* is incompatible with parameter of type char*

How do I fix this and make the argument no longer const?

5

There are 5 best solutions below

2
On

How do I fix this?

Use a different approach, use a stringstream instead or an auxiliary buffer.

void ClassName::toString(string& returnString)
{
    std::stringstream ss;
    ss << "Position: (" << position.x << ", " << position.y << ", " 
       << position.z << ")\n");
    returnString = ss.str();
}

and make the argument no longer const?

Don't. Hell will break lose if you change c_str to a non-const type. Fellow programmers will weep. Angels will lose their wings. Kittens will die. Oil prices will rise. Stock-market crashes. Zombie apocalipse.

It's const for a reason - so you don't modify it. Doing so would result in undefined behaviour.

0
On

http://www.cplusplus.com/reference/string/string/c_str/

The returned array points to an internal location with the required storage space for this sequence of characters plus its terminating null-character, but the values in this array should not be modified in the program and are only guaranteed to remain unchanged until the next call to a non-constant member function of the string object.

you are not allowed to modify the content of that returned char *

try something like:

void ClassName::toString(string& returnString)
{
    char tmp[256];
    sprintf(tmp, "Position: (%f, %f, %f)\n", position.x, position.y, position.y);
    returnString=tmp;
}
0
On

In modern C++ you could say:

std::string ClassName::toString() const
{
    return "Position: (" + std::to_string(position.x) + ", "
          + std::to_string(position.y) + ", "
          + std::to_string(position.z) + ")\n";
}

If you must use printf, you can still use a string, but you have to resize it first.

std::string ClassName::toString() const
{
    static const int initial_size = 1024;

    std::string s(initial_size);
    int ret = std::snprintf(&s[0], s.size(), "Position: (%f, %f, %f)\n", position.x, position.y, position.y);
    s.resize(ret);

    // handle overflow: print again
    if (s.size() > initial_size)
    {
        std::snprintf(&s[0], s.size(), "Position: (%f, %f, %f)\n", position.x, position.y, position.y);
    }
    return s;
}

Note that &s[0] gives you a pointer to mutable char, and indeed to the first element of a whole array of those of size s.size().

0
On

c_str() returns a const char * because it is generally not a good idea to change the value of the string using the pointer returned by c_str() function.

What you are trying to do is pointless by the way. You should simply use returnString.append() method.

But if you must do it with sprintf, you should make sure your string has the space to hold the output of sprintf.

First method: You can do it with .reserve() method, but it will not change the string's internal size counter which is returned by .size() if you directly modify the string buffer with sprintf() method (append() will do the proper size adjustment). There will not be any problems if you do not need to use the .size() method as far as I know.

Second method (better): This is the better way to go if you know the exact output length of the sprintf. You should call .resize() method instead of .reserve(). .resize() will properly adjust string's size counter.

Finally, you should use: sprintf(const_cast<char *>(returnString.data()), ...

This whole thing is not a good idea by the way.

P.S. .data() is the same as .**c**_str() except it does not append null terminator to make it c-style string.

0
On

Make a buffer for sprintf. Then assign to returnString back.

void ClassName::toString(string& returnString)
{ 
    char buffer[64] = {}; // expect the length of `Position Info` will not exceed 63
    sprintf(buffer, "Position: (%f, %f, %f)\n", position.x, position.y, position.y);
    returnString = buffer;
}