C++ exceptions are broken.

572 views
Skip to first unread message

Mr Flibble

unread,
Mar 8, 2016, 7:40:12 AM3/8/16
to

C++ exceptions are broken.

As it currently stands a more serious std::logic_error exception can be
downgraded into a less serious std::runtime_error exception if an
exception is thrown whilst evaluating the throw expression. This is a
nonsense.

Fix: if an exception is thrown whilst evaluating a throw expression then
std::terminate() is called.

/Flibble


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

James K. Lowden

unread,
Mar 9, 2016, 9:30:19 AM3/9/16
to

On Tue, 8 Mar 2016 06:31:12 CST
Mr Flibble <flibbleREM...@i42.co.uk> wrote:

> As it currently stands a more serious std::logic_error exception can
> be downgraded into a less serious std::runtime_error exception if an
> exception is thrown whilst evaluating the throw expression.

Could you be more specific, please? logic_error is mentioned in my
copy of the C++ only in section 19.2.1 (per the index), and I don't
find any mention of "downgrading". I also don't understand how it
*could* happen, since the compiler generates exception-handling
instructions, and the language per se doesn't define logic_error.

Why do you say logic_error is "more serious" than runtime_error?
Semantically that's not true, insofar as seriousness is not defined
by the library. I guess you mean that -- assuming they're used as
intended -- a logic error could have been avoided by the programmer,
whereas a runtime error could not. Depending on the situation, though,
either one can leave the user high and dry.

--jkl

Elias Salomão Helou Neto

unread,
Mar 12, 2016, 7:30:16 AM3/12/16
to

> Why do you say logic_error is "more serious" than runtime_error?
> Semantically that's not true, insofar as seriousness is not defined
> by the library. I guess you mean that -- assuming they're used as
> intended -- a logic error could have been avoided by the programmer,
> whereas a runtime error could not. Depending on the situation,
though,
> either one can leave the user high and dry.

I agree that there is a semantic problem in saying that a logic error is
more serious. But still, having a logic_error exception turned into a
runtime_error exception seems wrong to me. However, it sounds like one
should actually not write throw expressions that can throw another
exception. This is what should be considered bad practice, IMO.

The solution? Write simple throw expressions that cannot themselves
throw an exception. Am I right, wrong, or is this impossible altogether?

Best,
Elias.

Mr Flibble

unread,
Mar 12, 2016, 4:00:15 PM3/12/16
to

On 09/03/2016 14:25, James K. Lowden wrote:
>
> On Tue, 8 Mar 2016 06:31:12 CST
> Mr Flibble <flibbleREM...@i42.co.uk> wrote:
>
>> As it currently stands a more serious std::logic_error exception can
>> be downgraded into a less serious std::runtime_error exception if an
>> exception is thrown whilst evaluating the throw expression.
>
> Could you be more specific, please? logic_error is mentioned in my
> copy of the C++ only in section 19.2.1 (per the index), and I don't
> find any mention of "downgrading". I also don't understand how it
> *could* happen, since the compiler generates exception-handling
> instructions, and the language per se doesn't define logic_error.

Typically when constructing an exception derived from std::logic_error a

std::string object is created for the exception message which can cause
std::bad_alloc (a std::runtime_error exception) to be thrown replacing
the originally intended exception.

>
> Why do you say logic_error is "more serious" than runtime_error?
> Semantically that's not true, insofar as seriousness is not defined
> by the library. I guess you mean that -- assuming they're used as
> intended -- a logic error could have been avoided by the programmer,
> whereas a runtime error could not. Depending on the situation,
though,
> either one can leave the user high and dry.

In general std::logic_error is more serious then std::runtime_error
because you can often recover from a std::runtime_error but a
std::logic_error exception is often a sign that something is seriously
screwed and the only safe course of action is to terminate the process.
I treat std::logic_error based exceptions as fatal conditions because
of this.

/Flibble

Hergen Lehmann

unread,
Mar 12, 2016, 6:00:14 PM3/12/16
to

Am 12.03.2016 um 21:46 schrieb Mr Flibble:

> In general std::logic_error is more serious then std::runtime_error
> because you can often recover from a std::runtime_error but a
> std::logic_error exception is often a sign that something is seriously
> screwed and the only safe course of action is to terminate the
process.

I don't see any basis for that assumption.

std::logic_error includes stuff like std::domain_error (which is not
even used by the STL) or std::invalid_argument, which might be some
completely harmless rejection of input data.
On the other hand, std::runtime_error includes std::system_error, which
might be quite hazardous depending on which system operation did fail.

Hergen

James K. Lowden

unread,
Mar 13, 2016, 5:00:12 PM3/13/16
to

On Sat, 12 Mar 2016 14:46:06 CST
Mr Flibble <flibbleREM...@i42.co.uk> wrote:

> > I also don't understand how it *could* happen, since the compiler
> > generates exception-handling instructions, and the language per se
> > doesn't define logic_error.
>
> Typically when constructing an exception derived from
> std::logic_error a std::string object is created for the exception
> message which can cause std::bad_alloc (a std::runtime_error
> exception) to be thrown replacing the originally intended exception.

Oh, so you mean that the programmer may do something in handling the
exception to convert logic_error to runtime_error. Sure.

The programmer also has control over that. If it's important to pass
logic_error up the stack unmolested, do not allocate any objects in the
handler, or wrap that logic in its own try block. I've heard that in
some shops it's standard practice to use only static strings in
handlers to avoid the situation you describe.

>From my point of view, if std::string triggers bad_alloc, you're hip
deep in alligators because your heap is corrupted. Likely is the
logic_error is itself spurious, a knock-on effect of your pointers
spending their spring break in Fort Lauderdale. So, yeah, grap
__FILE__ and __LINE__, and head for the exit as soon as possible.

--jkl

Gareth Owen

unread,
Mar 15, 2016, 9:50:17 AM3/15/16
to

Mr Flibble <flibbleREM...@i42.co.uk> writes:

> std::string object is created for the exception message which can
> cause std::bad_alloc (a std::runtime_error exception) to be thrown
> replacing the originally intended exception.

std::bad_alloc is not a runtime_error (needing a string constructor
would be particularly idiotic for bad_alloc), but the point still holds.

Gareth Owen

unread,
Mar 15, 2016, 9:50:17 AM3/15/16
to

"James K. Lowden" <jklo...@speakeasy.net> writes:

> Oh, so you mean that the programmer may do something in handling the
> exception to convert logic_error to runtime_error. Sure.

No, the problem is

throw std::logic_error("I've made a terrible mistake");

calls the constructor

std::logic_error(const std::string&);

which has to construct a string, which might throw std::bad_alloc

Bo Persson

unread,
Mar 15, 2016, 1:30:12 PM3/15/16
to

On 2016-03-15 15:45, Gareth Owen wrote:
>
> "James K. Lowden" <jklo...@speakeasy.net> writes:
>
>> Oh, so you mean that the programmer may do something in handling the
>> exception to convert logic_error to runtime_error. Sure.
>
> No, the problem is
>
> throw std::logic_error("I've made a terrible mistake");
>
> calls the constructor
>
> std::logic_error(const std::string&);
>
> which has to construct a string, which might throw std::bad_alloc
>
>

There is also a constructor

explicit logic_error(const char*);

which doesn't have to call a string constructor.



Bo Persson

Alf P. Steinbach

unread,
Mar 17, 2016, 7:50:13 AM3/17/16
to

On 15.03.2016 19:22, Bo Persson wrote:
>
> There is also a constructor
>
> explicit logic_error(const char*);
>
> which doesn't have to call a string constructor.

Oh, it has to allocate memory dynamically for the string if it exceeds
some buffer size, because there is no requirement that the actual
argument points to a string that will outlive the exception object.

Most probably any particular implementation delegates the dynamic
allocation job to `std::string`.

However, considering that `what` is virtual function that produces a
`char const*`, it's not difficult at all to create a derived exception
class that doesn't do dynamic allocation.


Cheers!,

- Alf

Paavo Helde

unread,
Mar 17, 2016, 7:50:13 AM3/17/16
to

On 15.03.2016 20:22, Bo Persson wrote:
>
> On 2016-03-15 15:45, Gareth Owen wrote:
>>
>> "James K. Lowden" <jklo...@speakeasy.net> writes:
>>
>>> Oh, so you mean that the programmer may do something in handling the
>>> exception to convert logic_error to runtime_error. Sure.
>>
>> No, the problem is
>>
>> throw std::logic_error("I've made a terrible mistake");
>>
>> calls the constructor
>>
>> std::logic_error(const std::string&);
>>
>> which has to construct a string, which might throw std::bad_alloc
>>
>>
>
> There is also a constructor
>
> explicit logic_error(const char*);
>
> which doesn't have to call a string constructor.

The const char* argument is not guaranteed to be a static string so it
still needs to be copied over, allocating an arbitrarily large amount of
memory.

The standard also says:

logic_error(const char* what_arg);
4 Effects: Constructs an object of class logic_error.
5 Postcondition: strcmp(what(), what_arg) == 0.

The post-condition makes it illegal to ignore or truncate the argument
string, thus leaving an exception as the only conforming way to cope
with memory exhaustion.

It is interesting to note that MSVC implementation for example appears
to be not standard conforming and just ignores the argument if the
memory allocation fails.
Reply all
Reply to author
Forward
0 new messages