1

Say I've written some function myFunc that can throw const char* exceptions:

void myFunc()
{
    int returnCode = whatever();
    if (!returnCode)
    {
        std::string msg;
        msg.append("whatever function failed");
        std::cerr << msg << std::endl; // print warning message
        throw msg.c_str(); // throw warning message as exception
    }
}

And later I'm using it like so:

void myProgram()
{
    try
    {
        myFunc();
    }
    catch(const char* str)
    {
        // is 'str' string memory valid here?
    }
}

I realize this isn't really a good strategy for exception usage: better to throw and catch exception classes, not strings. But I'm curious about the scoping involved here.

Mike Clark
  • 10,027
  • 3
  • 40
  • 54

5 Answers5

2

msg.str() returns a temporary std::string. As temporaries are deleted at the end of a statement ;, the contents of the character string returned by c_str() become undefined when the throw ... ; statement terminates by leaving the scope via the exception mechanism.

(The lifetime of the const char* temporary is obviously extended to reach to the catch handler, but that does not help since the underlying buffer is gone).


Throwing std::string (i.e. throw msg.str();) would work, the lifetime of the temporary would be extended as intended.

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
  • Sorry, my example program was not focused enough because it used `ostringstream` in addition to `string`. I modified it to use just `string`, so we can ignore the implications of using `ostringstream`. Thanks! – Mike Clark May 04 '11 at 17:56
  • 2
    The lifetime of the temporary would not be extended, but since `throw`ing copies what's being thrown, that's not a problem. (When throwing a `char const*`, of course, what is copied is only the pointer, not what it points to.) – James Kanze May 04 '11 at 17:56
  • 2
    @Mike Clark The advice remains essentially the same - prefer to throw `std::string`. You can use `const char*` only safely if it points to a statically allocated memory area which is still valid at the catch-site. `throw "whatever function failed"` would therefore be fine. – Alexander Gessler May 04 '11 at 17:58
  • What if the creation of the temporary `std::string` object threw an exception? See my answer (and suggested solution). – Unsigned Jul 14 '12 at 23:40
1

Something Alexander Gessler did not mention in his answer, is the possibility of an exception being thrown by by std::string itself during the creation of the temporary string object.

std::exception is guaranteed not to throw an exception during construction, std::string has no such guarantee.

An alternative approach (for classes) is to declare a private std::string object in your class. Assemble the error message prior to the throw, and then throw the c_str(). This will throw a const char* exception, the error message being valid until the next exception is thrown from that class (which would presumably modify the error string again.)

A simple example can be found here: http://ideone.com/d9HhX

Community
  • 1
  • 1
Unsigned
  • 9,640
  • 4
  • 43
  • 72
1

Indeed the c_str() call is acting on a temporary (string) object and the pointer will be invalid when you catch it.

Not only that, but since the stringstream and stringcould do allocation, you need to make sure that you're not throwing because of heap problems. If you're at that point due to being out of memory you may bomb out even worse trying to create your exception. You generally want to avoid heap allocation during exceptional cases.

Are you unable to use say runtime_error or create your own exception type?

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Sorry, my example program was not focused enough because it used `ostringstream` in addition to `string`. I modified it to use just `string`, so we can ignore the implications of using `ostringstream`. We are indeed converting to use real exception classes, and will develop a pattern of using runtime_error in case of allocation problems. However, I still am curious about this. Thanks! – Mike Clark May 04 '11 at 17:59
1

Note that if you had said:

throw "error";

you would be OK because the lifetime of the string literal is the lifetime of the program. But don't do it, anyway!

0

Although this was answered over ten years ago, I would give some supplement:

https://learn.microsoft.com/en-us/cpp/cpp/exceptions-and-stack-unwinding-in-cpp

In the C++ exception mechanism, control moves from the throw statement to the first catch statement that can handle the thrown type. When the catch statement is reached, all of the automatic variables that are in scope between the throw and catch statements are destroyed in a process that is known as stack unwinding.

That's to say, any variables in the stack, including object variable, or basic type(int, char, etc) will be destroyed.

When throw an object in c++, c++ will actually make a cloned object by invoking the object copy constructor, the cloned object lifetime will valid through throw-catch.

reference: C++: Throwing an exception invokes the copy constructor?

Now, return to the OP question, so, "throw msg.c_str();" is invalid because the msg object is deleted (it's a auto variable), it happens to work because the memory is still there but actually freed.

So, just as @AlexanderGessler, @MarkB suggested, the best c++ practice is: always throw string object, runtime_error, instead of throwing string.c_str()

chenzero
  • 57
  • 1
  • 8