subclassing std::exception, where to store what() error string?

A

Andrew Tomazos

Please consider the below class MyError. My question is is it safe/
correct to store a std::string in a std::exception subclass and then
return string::c_str() on exception::what(). If not what is the
correct way to do this then? Thanks, Andrew.

#include <exception>
#include <string>
#include <sstream>

using namespace std;

class MyError : public exception
{
public:
MyError(A a, B b, C c)
{
ostringstream os;

os << "MyError occured because A =" << a.str() << ", B = " <<
b.str() << " and C = " << c.str() << endl;

m_sWhat = os.str();
}

virtual const char* what() const throw() { return
m_sWhat.c_str(); }

virtual ~MyError() throw() {}

private:
string m_sWhat;
};
 
A

Alf P. Steinbach

Please consider the below class MyError. My question is is it safe/
correct to store a std::string in a std::exception subclass and then
return string::c_str() on exception::what().

If you're concerned about memory exhaustion, no.

If not what is the correct way to do this then?

With the facilities of the standard library the only Really Safe
approach I can think of is to use a custom allocator for
`std::basic_string`, where that allocator uses a fixed special purpose
buffer, and impose a limit on the size of exception message.

However, as a practical matter I just derive from `std::runtime_error`,
and let that class deal with the string storage.

When you do that it's important to keep it in mind wrt. how to deal with
memory allocation failures / memory exhaustion.


Cheers & hth.,

- Alf
 
A

AnonMail2005

Please consider the below class MyError.  My question is is it safe/
correct to store a std::string in a std::exception subclass and then
return string::c_str() on exception::what().  If not what is the
correct way to do this then?  Thanks, Andrew.

#include <exception>
#include <string>
#include <sstream>

using namespace std;

class MyError : public exception
{
public:
        MyError(A a, B b, C c)
        {
                ostringstream os;

                os << "MyError occured because A =" << a.str() << ", B = " <<
b.str() << " and C = " << c.str() << endl;

                m_sWhat = os.str();
        }

    virtual const char* what() const throw() { return
m_sWhat.c_str(); }

    virtual ~MyError() throw() {}

private:
        string m_sWhat;

};

That's fine. That's what I've done in my own exception classes and it
works fine.
 
G

Goran

Please consider the below class MyError.  My question is is it safe/
correct to store a std::string in a std::exception subclass and then
return string::c_str() on exception::what().

This is safe. You should derive for runtime_error that does that
anyhow. If you do so, MyError will be even simpler. In fact, given
what MyError brings to the table, you probably just replace it with
runtime_error.

Beware, though, that, with your MyError, or anything you derive from
runtime_error, you might run out of memory when throwing that
exception, because you're allocating a string in the constructor.
That, however, should not affect code that's well-designed WRT
exceptions. The only downside might be that you try to throw MyError
to signal an error, but you end up catching bad_alloc. I believe, if
OOM happens, it's rare that you will care about missing that MyError.

Goran.
 
J

Juha Nieminen

Goran said:
Beware, though, that, with your MyError, or anything you derive from
runtime_error, you might run out of memory when throwing that
exception, because you're allocating a string in the constructor.
That, however, should not affect code that's well-designed WRT
exceptions. The only downside might be that you try to throw MyError
to signal an error, but you end up catching bad_alloc. I believe, if
OOM happens, it's rare that you will care about missing that MyError.

Actually if an exception is thrown while another exception is being
handled, bad things happen. (I think that abort() will be called.)
That's why exceptions shouldn't allocate memory.
 
G

Goran

  Actually if an exception is thrown while another exception is being
handled, bad things happen. (I think that abort() will be called.)
That's why exceptions shouldn't allocate memory.

I think that throwing from exception's constructor is OK, because no
exception is being handled at the time you construct the exception
object. Sure, you see e.g. "throw xyz(params)", but that's actually

xyz::xyz unnamed_exception_object(params);
throw unnamed_exception_object;

.... where throw was preceded by another, so it's as if it was never
reached.

Is there any legalese on that? Honest question.

Goran.
 
N

none

If you're concerned about memory exhaustion, no.



With the facilities of the standard library the only Really Safe
approach I can think of is to use a custom allocator for
`std::basic_string`, where that allocator uses a fixed special purpose
buffer, and impose a limit on the size of exception message.

However, as a practical matter I just derive from `std::runtime_error`,
and let that class deal with the string storage.

When you do that it's important to keep it in mind wrt. how to deal with
memory allocation failures / memory exhaustion.

Memory exhaustion will happen either:

N% of the time (with N close but not equal to 100):
Due to other things in the program so std::bad_alloc will be
thrown. You have to decide how to deal with it.

100-N%: due to attempting to allocate the string in MyError
constructor or maybe the stream operations. Again, in this case a
std::bad_alloc will be thrown *before* the MyError exception get
created and thrown which is correct and desirable behaviour.
An std::bad_alloc could happen while allocating memory for MyError
including all its members or while executing the constructor
(allocating string stream, etc). In either case, the MyError object
will not be created, the bad_alloc will be thrown before complete
creation of a MyError object. No MyError will be thrown. You will
again have to deal with the bad_alloc.

So if memory exhaustion happens, you will get a std::bad_alloc thrown
and will have to deal with it with the normal complexitiy of dealing
with memory exhaustion. Since memory exhaustion is probably a more
critical error than anything else, I think having a std::bad_alloc
thrown is absolutely desireable.

The only edge case would be if throwing an exception that used static
memory would result in enough of the stack being unrolled and
destructor freeing memory to aleviate the previously extremely low
memory condition. But this is an even more unlikely edge case that
you could not rely on in a general sense.

So I don't much problems introduced by the proposed MyError exception
class (assuming that you are not attempting to use it to replace
std::bad_alloc).

Yannick
 
J

Joshua Maurice

I think that throwing from exception's constructor is OK, because no
exception is being handled at the time you construct the exception
object. Sure, you see e.g. "throw xyz(params)", but that's actually

xyz::xyz unnamed_exception_object(params);
throw unnamed_exception_object;

... where throw was preceded by another, so it's as if it was never
reached.

Is there any legalese on that? Honest question.

I'm pretty sure your interpretation is correct. Before the exception
constructor exits, the throw statement has not "fully evaluated", so
no exception is pending, so if you throw from the exception
constructor, you're fine. At least, I'd be highly surprised if it went
any other way.
 
F

Fedor Rodikov

I'm pretty sure your interpretation is correct. Before the exception
constructor exits, the throw statement has not "fully evaluated", so
no exception is pending, so if you throw from the exception
constructor, you're fine. At least, I'd be highly surprised if it went
any other way.

Hi!

If an exception is thrown during a copy-construction of any exception
object then a program can be terminated.
The copy of exception object can be made after throw statement
(internally) or in catch statement.
I think this example isn't safe.
 
J

Joshua Maurice

Hi!

If an exception is thrown during a copy-construction of any exception
object then a program can be terminated.
The copy of exception object can be made after throw statement
(internally) or in catch statement.
I think this example isn't safe.

I think I was correct but not clear enough, and you are more correct.
Consider:

struct Foo { Foo() { throw 1; } };
int main()
{
try
{
throw Foo();
}
catch (int& )
{
return 1;
}
catch (Foo& )
{
return 2;
}
return 0;
}

In that code, it has perfectly defined behavior. It's going to return
1. That's all I was trying to say.

You did correctly note the FAQ.
http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.17
So, it's possible that during exception handling of the OP's example,
a bad_alloc will be thrown, and the program will die. At least, that's
how I understand it.

Before, exception handling was allowed to copy the exception object
any number of times. I'm wonder if C++11 has changed the rules to
require using move construction over copy construction if available in
this case. Because if so, then you won't get bad_alloc thrown as the
OP's exception is move-able.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top