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

Discussion in 'C++' started by Andrew Tomazos, Nov 27, 2011.

  1. 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;
    };
     
    Andrew Tomazos, Nov 27, 2011
    #1
    1. Advertising

  2. On 27.11.2011 12:07, Andrew Tomazos wrote:
    > 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
     
    Alf P. Steinbach, Nov 27, 2011
    #2
    1. Advertising

  3. Andrew Tomazos

    Guest

    On Nov 27, 6:07 am, Andrew Tomazos <> wrote:
    > 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.
     
    , Nov 27, 2011
    #3
  4. Andrew Tomazos

    Goran Guest

    On Nov 27, 12:07 pm, Andrew Tomazos <> wrote:
    > 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.
     
    Goran, Nov 28, 2011
    #4
  5. Goran <> wrote:
    > 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.
     
    Juha Nieminen, Nov 28, 2011
    #5
  6. Andrew Tomazos

    Goran Guest

    On Nov 28, 11:59 am, Juha Nieminen <> wrote:
    > Goran <> wrote:
    > > 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.


    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.
     
    Goran, Nov 28, 2011
    #6
  7. Andrew Tomazos

    none Guest

    In article <jatalp$607$>,
    Alf P. Steinbach <> wrote:
    >On 27.11.2011 12:07, Andrew Tomazos wrote:
    >> 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.


    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
     
    none, Nov 28, 2011
    #7
  8. On Nov 28, 3:19 am, Goran <> wrote:
    > On Nov 28, 11:59 am, Juha Nieminen <> wrote:
    >
    > > Goran <> wrote:
    > > > 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.

    >
    > 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.
     
    Joshua Maurice, Nov 28, 2011
    #8
  9. On Nov 29, 1:33 am, Joshua Maurice <> wrote:
    > On Nov 28, 3:19 am, Goran <> wrote:
    >
    >
    >
    >
    >
    >
    >
    >
    >
    > > On Nov 28, 11:59 am, Juha Nieminen <> wrote:

    >
    > > > Goran <> wrote:
    > > > > 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.

    >
    > > 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.


    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.
     
    Fedor Rodikov, Dec 2, 2011
    #9
  10. On Dec 2, 4:45 am, Fedor Rodikov <> wrote:
    > On Nov 29, 1:33 am, Joshua Maurice <> wrote:
    >
    > > > 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.

    >
    > 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.
     
    Joshua Maurice, Dec 2, 2011
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Peter Jansson
    Replies:
    5
    Views:
    6,318
    Ivan Vecerina
    Mar 17, 2005
  2. Geoffrey S. Knauth
    Replies:
    6
    Views:
    1,003
    Earl Purple
    Jan 18, 2006
  3. Fei Liu
    Replies:
    9
    Views:
    447
  4. Jeffrey Walton
    Replies:
    10
    Views:
    943
    Mathias Gaunard
    Nov 26, 2006
  5. petertwocakes

    Subclassing std::string confusion?

    petertwocakes, Nov 28, 2009, in forum: C++
    Replies:
    2
    Views:
    746
    petertwocakes
    Nov 28, 2009
Loading...

Share This Page