Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Which exception for a buffer overrun?

129 views
Skip to first unread message

Frederick Gotham

unread,
Oct 15, 2019, 6:00:44 AM10/15/19
to

Do you use out_of_range when there's a buffer overrun? It seems to be the closest

melzzzzz

unread,
Oct 15, 2019, 6:53:05 AM10/15/19
to
Frederick Gotham <cauldwel...@gmail.com> Wrote in message:
> Do you use out_of_range when there's a buffer overrun? It seems to be the closest

I decided not to use exceptions in new programs...
--
press any key to continue or any other to quit...



----Android NewsGroup Reader----
http://usenet.sinaapp.com/

Paavo Helde

unread,
Oct 15, 2019, 6:54:45 AM10/15/19
to
On 15.10.2019 13:00, Frederick Gotham wrote:
>
> Do you use out_of_range when there's a buffer overrun? It seems to be the closest

A buffer overrun should be considered a bug in the program and should be
dealt with by abort() or similar, not by an exception. If an exception
is thrown, it should be a "fatal" one, e.g. InternalFatalError, causing
the error to be prominently logged/reported so it can be forwarded to
the program maintainer for fixing, and the program terminated or at
least returned the the main GUI/UI loop.

There is no point in throwing a special exception like out_of_range
because the calling code has no way to fix its code to pass the correct
index (if it could do that, it could have passed the correct index the
first time).

If the calling code is just querying the length of an array or
something, then this should never trigger exceptions. First, using
exceptions for non-exceptional situations would convolute the program
logic, and secondly it would be dead slow.

It's true that there is std::vector::at() which throws
std::out_of_range, derived from std::logic_error, but many people
consider the whole std::logic_error exception class hierarchy as an
historical mistake.


Frederick Gotham

unread,
Oct 15, 2019, 7:08:17 AM10/15/19
to
On Tuesday, October 15, 2019 at 11:54:45 AM UTC+1, Paavo Helde wrote:

> A buffer overrun should be considered a bug in the program and should be
> dealt with by abort() or similar, not by an exception.


So if I use a scissors to cut a slice of bread, am I abusing the scissors?

Are you one of those people who will only use something for the one specific use that it says on the box?

I'm curious what your stance is on exception throwing in recursive functions (instead of conditional return statements)?

Fred. Zwarts

unread,
Oct 15, 2019, 7:09:21 AM10/15/19
to
Op 15.okt..2019 om 12:54 schreef Paavo Helde:
> On 15.10.2019 13:00, Frederick Gotham wrote:
>>
>> Do you use out_of_range when there's a buffer overrun? It seems to be
>> the closest
>
> A buffer overrun should be considered a bug in the program and should be
> dealt with by abort() or similar, not by an exception. If an exception
> is thrown, it should be a "fatal" one, e.g. InternalFatalError, causing
> the error to be prominently logged/reported so it can be forwarded to
> the program maintainer for fixing, and the program terminated or at
> least returned the the main GUI/UI loop.
>
> There is no point in throwing a special exception like out_of_range
> because the calling code has no way to fix its code to pass the correct
> index (if it could do that, it could have passed the correct index the
> first time).
>

Whether a buffer overrun is fatal depends on the circumstances. E.g.,
when a web server receives a request from a client that would cause a
buffer overrun and the server detects it in time, then there is no
reason to abort the web server. It could throw an exception to trigger
the error handling which could e.g., close the connection with the client.

Jorgen Grahn

unread,
Oct 15, 2019, 7:12:38 AM10/15/19
to
On Tue, 2019-10-15, Frederick Gotham wrote:
>
> Do you use out_of_range when there's a buffer overrun? It seems to
> be the closest

I don't think it's always a good idea to squeeze your errors into the
standard hierarchy.

I wrote a class last week for parsing TIFF files. When fed a broken
file, one thing that can happen is that an IFD offset points outside
the file. But other errors include zero-length arrays, missing
mandatory information and so on.

I don't think it would make sense to use out_of_range there. I made
a TiffDecoder::Error exception instead, and subclassed that one.

The key question is: how would the caller use the information?
My user would just want to know that the TIFF data was unusable.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

David Brown

unread,
Oct 15, 2019, 7:36:08 AM10/15/19
to
Look carefully at what you wrote - "when a web server receives a request
from a client that *would/ cause a buffer overrun". There is no buffer
overrun - there is proper checking of unsafe incoming data, /avoiding/
any risk of overrun. How you handle this is up to you - if you think it
will happen often, make it part of normal processing. If you think it
is going to be very rare, and exception is fine.

This is very different from a /real/ buffer overrun. A buffer overrun
is a programming error. The bug might be a coding error, or it might be
a higher level error in the design (not checking unsafe data on its way in).

melzzzzz

unread,
Oct 15, 2019, 7:49:44 AM10/15/19
to
"Fred. Zwarts" <F.Zw...@KVI.nl> Wrote in message:
> Op 15.okt..2019 om 12:54 schreef Paavo Helde:> On 15.10.2019 13:00, Frederick Gotham wrote:>>>> Do you use out_of_range when there's a buffer overrun? It seems to be >> the closest> > A buffer overrun should be considered a bug in the program and should be > dealt with by abort() or similar, not by an exception. If an exception > is thrown, it should be a "fatal" one, e.g. InternalFatalError, causing > the error to be prominently logged/reported so it can be forwarded to > the program maintainer for fixing, and the program terminated or at > least returned the the main GUI/UI loop.> > There is no point in throwing a special exception like out_of_range > because the calling code has no way to fix its code to pass the correct > index (if it could do that, it could have passed the correct index the > first time).> Whether a buffer overrun is fatal depends on the circumstances. E.g., when a web server receives a request from a client that would cause a buffer overrun and the server detects it in time, then there is no reason to abort the web server. It could throw an exception to trigger the error handling which could e.g., close the connection with the client.

Buffer overrun is fatal error. Usually, it is not normal operation. When you receive data from socket, you specify size of buffer... If buffer is too small there will be overrun and program will crash, if you are lucky...

David Brown

unread,
Oct 15, 2019, 7:57:46 AM10/15/19
to
On 15/10/2019 13:08, Frederick Gotham wrote:
> On Tuesday, October 15, 2019 at 11:54:45 AM UTC+1, Paavo Helde
> wrote:
>
>> A buffer overrun should be considered a bug in the program and
>> should be dealt with by abort() or similar, not by an exception.
>
>
> So if I use a scissors to cut a slice of bread, am I abusing the
> scissors?

I'd say yes. Aim to use the right tool for the job. Use a poorer
choice of tool if you can't get the right tool, but only if there is no
option.

<https://www.youtube.com/watch?v=B84pS6JTI9A>

>
> Are you one of those people who will only use something for the one
> specific use that it says on the box?

I can't answer for Paavo, but I am one of these people who try to think
out the best way to handle a task.

>
> I'm curious what your stance is on exception throwing in recursive
> functions (instead of conditional return statements)?
>

In C++, throwing an exception is usually /massively/ slower than
returning a code. It can easily be thousands of times slower. And for
recursive functions, it probably scales in time with the depth of the
recursion - and may limit optimisations of the functions.

Exceptions are fine when the situation is very rare and you don't have
requirements for worst-case latency, or where there is no sensible way
to handle the situation (like running out of memory), or when efficiency
doesn't matter and they make the code simpler and clearer (perhaps for
network or file errors).

They are a bad idea for normal processing as an alternative to return
values.

(The balance here will change significantly once static exceptions are
part of the language - they will be a great deal more efficient, and a
great deal easier for compile-time checking to support, at the cost of
less flexibility).

David Brown

unread,
Oct 15, 2019, 7:59:48 AM10/15/19
to
On 15/10/2019 13:49, melzzzzz wrote:

<snip jumbled quotations>

>
> Buffer overrun is fatal error. Usually, it is not normal operation.
> When you receive data from socket, you specify size of buffer... If
> buffer is too small there will be overrun and program will crash, if
> you are lucky...
>

If your buffer is too small, it's because you have asked for too much
data in the socket receive - i.e., it's a bug in the code.

Scott Lurndal

unread,
Oct 15, 2019, 9:31:05 AM10/15/19
to
David Brown <david...@hesbynett.no> writes:
>On 15/10/2019 13:09, Fred. Zwarts wrote:
>> Op 15.okt..2019 om 12:54 schreef Paavo Helde:

>> Whether a buffer overrun is fatal depends on the circumstances. E.g.,
>> when a web server receives a request from a client that would cause a
>> buffer overrun and the server detects it in time, then there is no
>> reason to abort the web server. It could throw an exception to trigger
>> the error handling which could e.g., close the connection with the client.
>>
>
>Look carefully at what you wrote - "when a web server receives a request
>from a client that *would/ cause a buffer overrun". There is no buffer
>overrun - there is proper checking of unsafe incoming data, /avoiding/
>any risk of overrun. How you handle this is up to you - if you think it
>will happen often, make it part of normal processing. If you think it
>is going to be very rare, and exception is fine.
>
>This is very different from a /real/ buffer overrun. A buffer overrun
>is a programming error. The bug might be a coding error, or it might be
>a higher level error in the design (not checking unsafe data on its way in).

Which raises the question of how the OP is detecting the soi disant buffer overrun.

Jorgen Grahn

unread,
Oct 15, 2019, 10:48:23 AM10/15/19
to
(And destroy the HTTP Session object owning all related resources.)

Thanks; that was also the counter-example I was going to give.

Paavo Helde

unread,
Oct 15, 2019, 10:48:42 AM10/15/19
to
On 15.10.2019 14:08, Frederick Gotham wrote:
> On Tuesday, October 15, 2019 at 11:54:45 AM UTC+1, Paavo Helde wrote:
>
>> A buffer overrun should be considered a bug in the program and should be
>> dealt with by abort() or similar, not by an exception.
>
>
> So if I use a scissors to cut a slice of bread, am I abusing the scissors?

This bread is eaten or thrown away soon. Software OTOH tends to stick
around for years and decades, and will affect many people, both
maintainers and users. So any abuse, however slight, will multiply
thousand-fold.

>
> Are you one of those people who will only use something for the one specific use that it says on the box?

No, I have built water boilers from a couple of gillette blades :-) But
I did this in full knowledge that my solution had serious drawbacks and
should not be used universally.

>
> I'm curious what your stance is on exception throwing in recursive functions (instead of conditional return statements)?
>

I am very fond of RAII and exceptions. Some of my lower level code is
littered with throws. However, there is no throw out_of_range or catch
out_of_range anywhere in my code.

Instead, I use some ASSERT and DEBUG_ASSERT macros for checking
pre-conditions and post-conditions and reporting the failing code line.



Paavo Helde

unread,
Oct 15, 2019, 11:37:44 AM10/15/19
to
This is all fine, but the exception we are discussing would not be
std::out_of_range (which would be presumable translated to "500 Internal
Server Error"), but rather something like InvalidParameterException,
translated to "400 Bad Request".

melzzzzz

unread,
Oct 15, 2019, 12:49:04 PM10/15/19
to
Paavo Helde <myfir...@osa.pri.ee> Wrote in message:
> On 15.10.2019 17:48, Jorgen Grahn wrote:> On Tue, 2019-10-15, Fred. Zwarts wrote:>> Op 15.okt..201

Anyway, request too big shouldn't cause buffer overflow...

Sory, for not quoting, I have crappy freeware on phone...

Mr Flibble

unread,
Oct 15, 2019, 1:21:31 PM10/15/19
to
For detectable bugs I throw custom exceptions derived from
std::logic_error which are NOT caught (or are re-thrown) so the
application terminates using std::terminate (which in turn will, by
default, call abort()).Just calling abort() on its own without using
exceptions is a bit pants as there is no opportunity to log.

/Flibble

--
"Snakes didn't evolve, instead talking snakes with legs changed into
snakes." - Rick C. Hodgin

“You won’t burn in hell. But be nice anyway.” – Ricky Gervais

“I see Atheists are fighting and killing each other again, over who
doesn’t believe in any God the most. Oh, no..wait.. that never happens.” –
Ricky Gervais

"Suppose it's all true, and you walk up to the pearly gates, and are
confronted by God," Bryne asked on his show The Meaning of Life. "What
will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery
that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a
world that is so full of injustice and pain. That's what I would say."

Melzzzzz

unread,
Oct 15, 2019, 4:32:16 PM10/15/19
to
What's stopping you to log then call abort?
>
> /Flibble
>


--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala

Öö Tiib

unread,
Oct 15, 2019, 4:44:41 PM10/15/19
to
On Tuesday, 15 October 2019 13:00:44 UTC+3, Frederick Gotham wrote:
> Do you use out_of_range when there's a buffer overrun? It seems to be the closest

May be if what you write is rather abstract class like
boost::scoped_array and you implement at() function for it.
BTW boost::scoped_array does not have at().
On less abstract cases the buffer overrun usually means that
the input data is somehow damaged or corrupted. Then it is
better to throw something that tells that the protocol has
not been followed, byte sequence is illegal or arguments are
incorrect.

Mr Flibble

unread,
Oct 15, 2019, 5:44:04 PM10/15/19
to
Bad input data isn't a logic error.

Soviet_Mario

unread,
Oct 15, 2019, 5:59:18 PM10/15/19
to
mine is just a no more than a hobbist tinkerer's opinion,
but ...
even if I surely won't object to performance issues, another
(often contrasting) point of view would be readability and
cross-usability of code

I mean, often I feel the need of "consistent" (= UNIFORM)
error management.

Even if surely more performant, mixed management can be
difficult to understand and use.
So some importance maybe should also be given to the choice
of using the same tool systematically for every problem of
the same nature (errors), regardeless of its performance
potential problems.
Also "snippet" reuse is hindered by mixed use of both
exceptions and return types, and maybe it's more error prone.

One thing I really can't stand is the way C used to share a
single variable (errno) to store status, that may be
overwritten unpredictably




--
1) Resistere, resistere, resistere.
2) Se tutti pagano le tasse, le tasse le pagano tutti
Soviet_Mario - (aka Gatto_Vizzato)

Scott Lurndal

unread,
Oct 15, 2019, 6:13:35 PM10/15/19
to
Soviet_Mario <Sovie...@CCCP.MIR> writes:

>One thing I really can't stand is the way C used to share a
>single variable (errno) to store status, that may be
>overwritten unpredictably

Can you elaborate? It was well understood and documented when errno could
be modified (on any system or library call).

Paavo Helde

unread,
Oct 16, 2019, 12:57:53 AM10/16/19
to
On 16.10.2019 0:59, Soviet_Mario wrote:
> One thing I really can't stand is the way C used to share a single
> variable (errno) to store status, that may be overwritten unpredictably

It's not a single variable, it's a single variable per thread.

Yes, it may be overwritten by something seemingly innocent like a
std::string construction, but this would be just "unexpectedly" (for
some of us), not "unpredictably".

Errno may only be overwritten "unpredictably" if there is a signal
handler which does not take care to store and restore it properly. But
this would be a bug in the signal handler.

Jorgen Grahn

unread,
Oct 16, 2019, 2:20:58 AM10/16/19
to
On Tue, 2019-10-15, Melzzzzz wrote:
> On 2019-10-15, Mr Flibble <flibbleREM...@i42.co.uk> wrote:
...

>> For detectable bugs I throw custom exceptions derived from
>> std::logic_error which are NOT caught (or are re-thrown) so the
>> application terminates using std::terminate (which in turn will, by
>> default, call abort()).Just calling abort() on its own without using
>> exceptions is a bit pants as there is no opportunity to log.
>
> What's stopping you to log then call abort?

I think he means if he throws, upper layers of code can decide if it
wants to log or not. On the other hand, if it /does/ catch the
exception, stack unwinding will happen and there will be no
interesting core dump even if it re-throws.

I think I would log and then throw, and then in higher layers not
catch that exception.

Öö Tiib

unread,
Oct 16, 2019, 3:01:21 AM10/16/19
to
On Wednesday, 16 October 2019 00:44:04 UTC+3, Mr Flibble wrote:
> On 15/10/2019 21:44, Öö Tiib wrote:
> > On Tuesday, 15 October 2019 13:00:44 UTC+3, Frederick Gotham wrote:
> >> Do you use out_of_range when there's a buffer overrun? It seems to be the closest
> >
> > May be if what you write is rather abstract class like
> > boost::scoped_array and you implement at() function for it.
> > BTW boost::scoped_array does not have at().
> > On less abstract cases the buffer overrun usually means that
> > the input data is somehow damaged or corrupted. Then it is
> > better to throw something that tells that the protocol has
> > not been followed, byte sequence is illegal or arguments are
> > incorrect.
>
> Bad input data isn't a logic error.

The logic_error means that the error is because of possibly
preventable logical inconsistency in input. But how can
std::stoi() know that the out of range or illegal input
is coming from corrupt input data? Also for preventing the
inconsistency its caller has possibly to do most of what
stoi does itself.

David Brown

unread,
Oct 16, 2019, 4:17:13 AM10/16/19
to
Readability is of course very important - and code correctness even more
so. However, I am not convinced that exceptions are a great correctness
feature. It is hard to write fully exception-safe code. And, worse, it
is easy to write code that /appears/ to be exception safe, but is not.

The two key problems, as I see it, are first that exceptions means all
sorts of code (function calls and expressions) can fail unexpectedly at
any time, leading to early returns from your function. You have to be
thinking all the time about what will happen if there is an exception
when the code runs. This can mean making more RAII classes, or more
indirect references (via unique_ptr, for example), simply to ensure
cleanup in case functions throw an exception. That will make the code
safe and correct - but can be of significant readability cost.

The other problem is that a lot of the information about C++ exceptions
that a function can throw, is basically lying. You can /say/ that a
function only throws specific exception types, which would be very
useful, but it's a lie. At least C++11 "noexcept" is a stronger, and
now "throw" specifications are no longer part of C++17 at all. But
there is currently no way to say what exceptions a function really could
throw, no way to check such features at compile time, no integration
with the type system, and the default is for functions to be specified
as "this function could throw anything, or pass on any exception" rather
than the more sensible "this function won't throw - it will do what it
says it will do".

(Static exceptions will, hopefully, improve on this a lot.)

> I mean, often I feel the need of "consistent" (= UNIFORM) error
management.

Consistency is good - but not if it is artificial consistency just for
consistency's sake.

>
> Even if surely more performant, mixed management can be difficult to
> understand and use.
> So some importance maybe should also be given to the choice of using the
> same tool systematically for every problem of the same nature (errors),
> regardeless of its performance potential problems.
> Also "snippet" reuse is hindered by mixed use of both exceptions and
> return types, and maybe it's more error prone.
>
> One thing I really can't stand is the way C used to share a single
> variable (errno) to store status, that may be overwritten unpredictably
>

Yes, errno is a very questionable idea. It made sense when it was
introduced, but leads to many limitations. It does allow some useful
practices, however, such as doing a string of calculations and then
checking for errno at the end rather than checking after each calculation.

(It's not a single global any more - there is one errno per thread.)

Queequeg

unread,
Oct 16, 2019, 8:53:06 AM10/16/19
to
melzzzzz <m...@melzzzzz.com> wrote:

>> Do you use out_of_range when there's a buffer overrun? It seems to be the closest
>
> I decided not to use exceptions in new programs...

What is the reason?

--
https://www.youtube.com/watch?v=9lSzL1DqQn0

Öö Tiib

unread,
Oct 16, 2019, 8:54:55 AM10/16/19
to
Basically to live with exceptions all classes have to be RAII as bare
minimum. Yes, unique_ptr is one of the handy components for to
build (or on simpler case to typedef) such class but I don't
understand what readability cost there is. Can you bring example
what you meant?

> The other problem is that a lot of the information about C++ exceptions
> that a function can throw, is basically lying. You can /say/ that a
> function only throws specific exception types, which would be very
> useful, but it's a lie. At least C++11 "noexcept" is a stronger, and
> now "throw" specifications are no longer part of C++17 at all. But
> there is currently no way to say what exceptions a function really could
> throw, no way to check such features at compile time, no integration
> with the type system, and the default is for functions to be specified
> as "this function could throw anything, or pass on any exception" rather
> than the more sensible "this function won't throw - it will do what it
> says it will do".

The exception specifications are often a lie anyway ... or hackable around.
What we see in lot of bad code of languages where basically everything
may throw? Something like that (Java or C# code):

public void foo() throws Exception {/* ... */} // WTF?

So there have to be tools and policies in place for to use the exceptions
regardless of language. However it is not in any way better with checking
error codes of function "success" returns. Latter just adds lot of
required boilerplate code. Isn't that major readability cost?

> (Static exceptions will, hopefully, improve on this a lot.)
>
> > I mean, often I feel the need of "consistent" (= UNIFORM) error
> management.
>
> Consistency is good - but not if it is artificial consistency just for
> consistency's sake.

Every good project has to establish consistent project-wide error
handling policy. It is likely possible to make it company-
wide when company is working on closely related products
in narrow problem domain. However I can not imagine
programming-language-wide (or even industry-wide) error
handling policies. May be with some different programming
language (like Rust).

>
> >
> > Even if surely more performant, mixed management can be difficult to
> > understand and use.
> > So some importance maybe should also be given to the choice of using the
> > same tool systematically for every problem of the same nature (errors),
> > regardeless of its performance potential problems.
> > Also "snippet" reuse is hindered by mixed use of both exceptions and
> > return types, and maybe it's more error prone.
> >
> > One thing I really can't stand is the way C used to share a single
> > variable (errno) to store status, that may be overwritten unpredictably
> >
>
> Yes, errno is a very questionable idea. It made sense when it was
> introduced, but leads to many limitations. It does allow some useful
> practices, however, such as doing a string of calculations and then
> checking for errno at the end rather than checking after each calculation.
>
> (It's not a single global any more - there is one errno per thread.)

The errno is awful ... OTOH it seems to be the only semi-portable way
in C++ to figure out what went wrong with lot of things (like with that
std::basic_fstream::open() and the like) when it matters.

Queequeg

unread,
Oct 16, 2019, 9:00:50 AM10/16/19
to
Fred. Zwarts <F.Zw...@kvi.nl> wrote:

> Whether a buffer overrun is fatal depends on the circumstances. E.g.,
> when a web server receives a request from a client that would cause a
> buffer overrun and the server detects it in time, then there is no
> reason to abort the web server. It could throw an exception to trigger
> the error handling which could e.g., close the connection with the client.

Exception doesn't seem to be a good choice here, at least for me.
Exceptions are meant to handle exceptional situations, not an invalid user
input, even if this input, if not validated, would cause a buffer overrun
or other critical error.

Your code can / should safely continue after an invalid user input, and
handle it appropriately (by signalling an error, terminating the
connection, logging it -- but it should be defined).

In my code, I use exceptions only to signal assertions and situations that
should never happen if the code was correct, machine had enough memory,
and the code could safely continue. The only way to recover from such
exception is to log debugging information and gracefully die, not doing
any more harm.

--
https://www.youtube.com/watch?v=9lSzL1DqQn0

Soviet_Mario

unread,
Oct 16, 2019, 9:04:35 AM10/16/19
to
I must have mis-expressed. Unpredictably would have meant
that this variable can be async modified anywhere and
anytime (and in multithread context, or even in event-driven
programs, when a routine could be called amidst another call
is working, and that gets frozen and revived later, one
might find errno modified even just after tested in the last
instruction on the "local" flow).

And in that case even knowing in advance this may happen is
useless practically.
Sure : often this modification is not that relevant locally
(an error occurred elsewhere is more likely not affect the
reprise of the code frozen before) but not always.
Consider an error generated on a shared resource, like a
file/stream and so.
Routine A tests a file (probing errno after some operation)
before starting to worki and prepares to work on it

then an async event trigger invoke routine B (with A being
put on wait) which generates an error on the same file,
setting errno.
When resumed, routine A will assume wrongly that the file is
not corrupted, but actually is.

Actually even return value test does not seem (to me) to
protect much from this.
Exception is more resilient, at the cost of speed, as it
catch on the fly ex-post rather then test-and-act)

Soviet_Mario

unread,
Oct 16, 2019, 9:05:59 AM10/16/19
to
On 16/10/2019 06:57, Paavo Helde wrote:
> On 16.10.2019 0:59, Soviet_Mario wrote:
>> One thing I really can't stand is the way C used to share
>> a single
>> variable (errno) to store status, that may be overwritten
>> unpredictably
>
> It's not a single variable, it's a single variable per thread.

oh ! Sorry I was wrong then :\

>
> Yes, it may be overwritten by something seemingly innocent
> like a std::string construction, but this would be just
> "unexpectedly" (for some of us), not "unpredictably".
>
> Errno may only be overwritten "unpredictably" if there is a
> signal handler which does not take care to store and restore
> it properly. But this would be a bug in the signal handler.

I have been in error :\

David Brown

unread,
Oct 16, 2019, 10:12:40 AM10/16/19
to
On 16/10/2019 14:54, Öö Tiib wrote:
> On Wednesday, 16 October 2019 11:17:13 UTC+3, David Brown wrote:
>> On 15/10/2019 23:59, Soviet_Mario wrote:
>>> On 15/10/2019 13:57, David Brown wrote:

>>
>> Readability is of course very important - and code correctness even more
>> so. However, I am not convinced that exceptions are a great correctness
>> feature. It is hard to write fully exception-safe code. And, worse, it
>> is easy to write code that /appears/ to be exception safe, but is not.
>>
>> The two key problems, as I see it, are first that exceptions means all
>> sorts of code (function calls and expressions) can fail unexpectedly at
>> any time, leading to early returns from your function. You have to be
>> thinking all the time about what will happen if there is an exception
>> when the code runs. This can mean making more RAII classes, or more
>> indirect references (via unique_ptr, for example), simply to ensure
>> cleanup in case functions throw an exception. That will make the code
>> safe and correct - but can be of significant readability cost.
>
> Basically to live with exceptions all classes have to be RAII as bare
> minimum. Yes, unique_ptr is one of the handy components for to
> build (or on simpler case to typedef) such class but I don't
> understand what readability cost there is. Can you bring example
> what you meant?
>

I can try and give an outline of an example. Suppose you have code like
this, using a bool "success" return instead of exceptions:

bool success = tryThis();
doThisAnyway();
if (!success) return false;
continueProcessing();
return true;

How do you make that into an exception-based handling, with "tryThis"
throwing an exception on failure?

You can use a try block and re-throw:

try {
tryThis();
} catch (...) {
doThisAnyway();
throw();
}
doThisAnyway();
continueProcessing();

You've duplicated code as well as reducing the clarity.

Or you can make a class:

struct DoThisAnyway_later {
DoThisAnyway_later() {};
~DoThisAnyway_later() { doThisAnyway(); };
}

{
DoThisAnyway_later doThisAnywayLater();
tryThis();
// doThisAnyway called by destructor
}
continueProcessing();

Now you have re-arranged the order of the code, losing the correlation
between the textual order and the run-time order. (A general "scope
guard" template would reduce the code here, but not change the re-ordering.)


With explicit, manual control of the errors or unusual circumstances, it
can be easier to see the flow of the code.

And note that if you want to be sure that "doThisAnyway()" is done, and
"tryThis()" does not have a "noexcept" specifier, you have to have
something like these exception-safe codings just in case, because it
/might/ throw something. With a return-based solution, you know if it
is giving success feedback because it is in the return type (perhaps
something more sophisticated than a simple bool, such as a
std::optional, or a variant, or an "expected") - and you can mark it
with [[nodiscard]] to make sure the code checks the result.


I am not saying that exceptions /always/ make code harder to read - far
from it. I am merely saying that /sometimes/ they do.

(To be clear here, my kind of C++ programming is usually done with
exception support disabled in the compiler - partly for worst-case
performance reasons, partly for clarity, partly due to limited support
for some targets, partly due to a mix of C and C++ code. This limits my
experience of C++ exceptions.)

>> The other problem is that a lot of the information about C++ exceptions
>> that a function can throw, is basically lying. You can /say/ that a
>> function only throws specific exception types, which would be very
>> useful, but it's a lie. At least C++11 "noexcept" is a stronger, and
>> now "throw" specifications are no longer part of C++17 at all. But
>> there is currently no way to say what exceptions a function really could
>> throw, no way to check such features at compile time, no integration
>> with the type system, and the default is for functions to be specified
>> as "this function could throw anything, or pass on any exception" rather
>> than the more sensible "this function won't throw - it will do what it
>> says it will do".
>
> The exception specifications are often a lie anyway ... or hackable around.
> What we see in lot of bad code of languages where basically everything
> may throw? Something like that (Java or C# code):
>
> public void foo() throws Exception {/* ... */} // WTF?
>
> So there have to be tools and policies in place for to use the exceptions
> regardless of language. However it is not in any way better with checking
> error codes of function "success" returns. Latter just adds lot of
> required boilerplate code. Isn't that major readability cost?

Somewhere there is /always/ going to be a cost!

I think a lot of the readability cost of using function return values
for errors comes from the days of older C and older C compilers, where
you often had a mess of goto's in order to deal with errors underway
without having deeply nested "if" statements. Often you can get a lot
clearer code by simply dividing the code into several smaller functions,
with a style of "if (!success) return false;" as an exit. Before C99
"inline", and before compilers inlined single-use static functions
automatically, multiple small functions could quickly be costly in
efficiency.

And of course with C++ you have all the power of RAII, and tools like
std::unique_ptr - you don't need exceptions to use them. They apply
equally well to exiting functions with an early return.

>
>> (Static exceptions will, hopefully, improve on this a lot.)
>>
>>> I mean, often I feel the need of "consistent" (= UNIFORM) error
>> management.
>>
>> Consistency is good - but not if it is artificial consistency just for
>> consistency's sake.
>
> Every good project has to establish consistent project-wide error
> handling policy. It is likely possible to make it company-
> wide when company is working on closely related products
> in narrow problem domain. However I can not imagine
> programming-language-wide (or even industry-wide) error
> handling policies. May be with some different programming
> language (like Rust).
>

Certainly there is no one "ideal for every case" solution here.

>>
>>>
>>> Even if surely more performant, mixed management can be difficult to
>>> understand and use.
>>> So some importance maybe should also be given to the choice of using the
>>> same tool systematically for every problem of the same nature (errors),
>>> regardeless of its performance potential problems.
>>> Also "snippet" reuse is hindered by mixed use of both exceptions and
>>> return types, and maybe it's more error prone.
>>>
>>> One thing I really can't stand is the way C used to share a single
>>> variable (errno) to store status, that may be overwritten unpredictably
>>>
>>
>> Yes, errno is a very questionable idea. It made sense when it was
>> introduced, but leads to many limitations. It does allow some useful
>> practices, however, such as doing a string of calculations and then
>> checking for errno at the end rather than checking after each calculation.
>>
>> (It's not a single global any more - there is one errno per thread.)
>
> The errno is awful ... OTOH it seems to be the only semi-portable way
> in C++ to figure out what went wrong with lot of things (like with that
> std::basic_fstream::open() and the like) when it matters.
>

I am not an errno fan myself. But I can understand why it was made, and
that it could be useful for some kinds of coding.

melzzzzz

unread,
Oct 16, 2019, 12:29:56 PM10/16/19
to
quee...@trust.no1 (Queequeg) Wrote in message:
> melzzzzz <m...@melzzzzz.com> wrote:>> Do you use out_of_range when there's a buffer overrun? It seems to be the closest> > I decided not to use exceptions in new programs...

"What is the reason?"

I started to use discriminated unions, as it is better way of
handling errors...
there was recent discussion about that...
--
Press any key to continue or any other to quit....

Soviet_Mario

unread,
Oct 16, 2019, 2:45:46 PM10/16/19
to
the example is formally genial :)
But I wonder what could actually be inside.
I mean : a function like dothisanyway should be either
independent on the execution of tryThis result, or, if it is
just PARTIALLY dependent, then tryThis seems to have a too
coarse grain : it has internally a part tryThis depends upon
and another who don't, and it could be splitted logically in
two function.
The former who is actually to be put BEFORE doThisAnyway,
and the latter, possibly generating the error (on which
doThisAnyway does not depend) who can be placed AFTER IT


getting sth like

tryThisPart_1 ();
doThisAnyway ();
try {
tryThisPart_2 ();
} catch (...) {
// etc
};

Part_1 is essential to doThisAnyway
Part_2 is independent

The two Parts seem to me to be logically weakly correlated
... but it's just sort of an intuition, not really sure

Jorgen Grahn

unread,
Oct 16, 2019, 3:02:46 PM10/16/19
to
On Wed, 2019-10-16, Öö Tiib wrote:
> On Wednesday, 16 October 2019 00:44:04 UTC+3, Mr Flibble wrote:
>> On 15/10/2019 21:44, Öö Tiib wrote:
>> > On Tuesday, 15 October 2019 13:00:44 UTC+3, Frederick Gotham wrote:

>> >> Do you use out_of_range when there's a buffer overrun? It seems
>> >> to be the closest
>> >
>> > May be if what you write is rather abstract class like
>> > boost::scoped_array and you implement at() function for it.
>> > BTW boost::scoped_array does not have at().
>> > On less abstract cases the buffer overrun usually means that
>> > the input data is somehow damaged or corrupted. Then it is
>> > better to throw something that tells that the protocol has
>> > not been followed, byte sequence is illegal or arguments are
>> > incorrect.
>>
>> Bad input data isn't a logic error.
>
> The logic_error means that the error is because of possibly
> preventable logical inconsistency in input.

No; as I understand it it means faulty program logic, i.e. a
detectable bug, same as what the assert() macro is supposed to
be used for.

So since std::out_of_range is a std::logic_error, I guess you're not
supposed to use array::at() "speculatively".

Possibly I missed some context.

[snip]

Ian Collins

unread,
Oct 16, 2019, 3:15:44 PM10/16/19
to
The same set of conditions also apply to code that uses early returns.
Throwing an exception is just a special case of an early return.

> The other problem is that a lot of the information about C++ exceptions
> that a function can throw, is basically lying. You can /say/ that a
> function only throws specific exception types, which would be very
> useful, but it's a lie. At least C++11 "noexcept" is a stronger, and
> now "throw" specifications are no longer part of C++17 at all. But
> there is currently no way to say what exceptions a function really could
> throw, no way to check such features at compile time, no integration
> with the type system, and the default is for functions to be specified
> as "this function could throw anything, or pass on any exception" rather
> than the more sensible "this function won't throw - it will do what it
> says it will do".

This is true, but we are already there when we use the standard library
unless we build with exceptions disabled.

--
Ian.

David Brown

unread,
Oct 16, 2019, 5:00:47 PM10/16/19
to
On 16/10/2019 21:15, Ian Collins wrote:
> On 16/10/2019 21:16, David Brown wrote:

>> Readability is of course very important - and code correctness even more
>> so.  However, I am not convinced that exceptions are a great correctness
>> feature.  It is hard to write fully exception-safe code.  And, worse, it
>> is easy to write code that /appears/ to be exception safe, but is not.
>>
>> The two key problems, as I see it, are first that exceptions means all
>> sorts of code (function calls and expressions) can fail unexpectedly at
>> any time, leading to early returns from your function.  You have to be
>> thinking all the time about what will happen if there is an exception
>> when the code runs.  This can mean making more RAII classes, or more
>> indirect references (via unique_ptr, for example), simply to ensure
>> cleanup in case functions throw an exception.  That will make the code
>> safe and correct - but can be of significant readability cost.
>
> The same set of conditions also apply to code that uses early returns.
> Throwing an exception is just a special case of an early return.

There are two big differences. One is that with an early return,
everything is clear in the code - you know /exactly/ when the return
happens, without having to consider whether functions may or may not
throw. The second is that you have control of the return - you can
choose if it is early, or if other things are done first.

>
>> The other problem is that a lot of the information about C++ exceptions
>> that a function can throw, is basically lying.  You can /say/ that a
>> function only throws specific exception types, which would be very
>> useful, but it's a lie.  At least C++11 "noexcept" is a stronger, and
>> now "throw" specifications are no longer part of C++17 at all.  But
>> there is currently no way to say what exceptions a function really could
>> throw, no way to check such features at compile time, no integration
>> with the type system, and the default is for functions to be specified
>> as "this function could throw anything, or pass on any exception" rather
>> than the more sensible "this function won't throw - it will do what it
>> says it will do".
>
> This is true, but we are already there when we use the standard library
> unless we build with exceptions disabled.
>

Correct me if I am wrong, but for the most part the only exception that
will be thrown by the standard library is for failed memory allocations.
On many platforms, it is highly unlikely ever to happen - and for many
programs, the situation is so unexpected and critical that a dead crash
and error message is probably an acceptable way to handle it.

Ian Collins

unread,
Oct 16, 2019, 5:36:19 PM10/16/19
to
On 17/10/2019 10:00, David Brown wrote:
> On 16/10/2019 21:15, Ian Collins wrote:
>> On 16/10/2019 21:16, David Brown wrote:
>
>>> Readability is of course very important - and code correctness even more
>>> so.  However, I am not convinced that exceptions are a great correctness
>>> feature.  It is hard to write fully exception-safe code.  And, worse, it
>>> is easy to write code that /appears/ to be exception safe, but is not.
>>>
>>> The two key problems, as I see it, are first that exceptions means all
>>> sorts of code (function calls and expressions) can fail unexpectedly at
>>> any time, leading to early returns from your function.  You have to be
>>> thinking all the time about what will happen if there is an exception
>>> when the code runs.  This can mean making more RAII classes, or more
>>> indirect references (via unique_ptr, for example), simply to ensure
>>> cleanup in case functions throw an exception.  That will make the code
>>> safe and correct - but can be of significant readability cost.
>>
>> The same set of conditions also apply to code that uses early returns.
>> Throwing an exception is just a special case of an early return.
>
> There are two big differences. One is that with an early return,
> everything is clear in the code - you know /exactly/ when the return
> happens, without having to consider whether functions may or may not
> throw. The second is that you have control of the return - you can
> choose if it is early, or if other things are done first.

I was mainly responding to more of the "This can mean making more RAII
classes" section. Once you have early returns, objects instantiated in a
function have to be, for lack of a better term, self managing. This is
what applies equally to code with early returns and throws. Early
return safe and exception safe are pretty much synonymous.

>>> The other problem is that a lot of the information about C++ exceptions
>>> that a function can throw, is basically lying.  You can /say/ that a
>>> function only throws specific exception types, which would be very
>>> useful, but it's a lie.  At least C++11 "noexcept" is a stronger, and
>>> now "throw" specifications are no longer part of C++17 at all.  But
>>> there is currently no way to say what exceptions a function really could
>>> throw, no way to check such features at compile time, no integration
>>> with the type system, and the default is for functions to be specified
>>> as "this function could throw anything, or pass on any exception" rather
>>> than the more sensible "this function won't throw - it will do what it
>>> says it will do".
>>
>> This is true, but we are already there when we use the standard library
>> unless we build with exceptions disabled.
>>
>
> Correct me if I am wrong, but for the most part the only exception that
> will be thrown by the standard library is for failed memory allocations.

That is true, but even innocuous operations such as insertions can throw
if a constructor of the type being inserted throws. That's why I said
we have to build (every component of an application) with exceptions
disabled to truly grantee something won't throw.

--
Ian

Chris M. Thomasson

unread,
Oct 16, 2019, 5:44:18 PM10/16/19
to
On 10/15/2019 3:54 AM, Paavo Helde wrote:
> On 15.10.2019 13:00, Frederick Gotham wrote:
>>
>> Do you use out_of_range when there's a buffer overrun? It seems to be
>> the closest
>
> A buffer overrun should be considered a bug in the program and should be
> dealt with by abort() or similar, not by an exception.

Fwiw, I remember creating a plug in system where a user defined program
could crash. The watchdog process detected this, and tried to recover,
restart or label as dead. An interesting method to do this is via robust
mutexes.

Öö Tiib

unread,
Oct 16, 2019, 6:23:02 PM10/16/19
to
On Wednesday, 16 October 2019 22:02:46 UTC+3, Jorgen Grahn wrote:
> On Wed, 2019-10-16, Öö Tiib wrote:
> > On Wednesday, 16 October 2019 00:44:04 UTC+3, Mr Flibble wrote:
> >> On 15/10/2019 21:44, Öö Tiib wrote:
> >> > On Tuesday, 15 October 2019 13:00:44 UTC+3, Frederick Gotham wrote:
>
> >> >> Do you use out_of_range when there's a buffer overrun? It seems
> >> >> to be the closest
> >> >
> >> > May be if what you write is rather abstract class like
> >> > boost::scoped_array and you implement at() function for it.
> >> > BTW boost::scoped_array does not have at().
> >> > On less abstract cases the buffer overrun usually means that
> >> > the input data is somehow damaged or corrupted. Then it is
> >> > better to throw something that tells that the protocol has
> >> > not been followed, byte sequence is illegal or arguments are
> >> > incorrect.
> >>
> >> Bad input data isn't a logic error.
> >
> > The logic_error means that the error is because of possibly
> > preventable logical inconsistency in input.
>
> No; as I understand it it means faulty program logic, i.e. a
> detectable bug, same as what the assert() macro is supposed to
> be used for.

"The class logic_error defines the type of objects thrown as
exceptions to report errors presumably detectable before the
program executes, such as violations of logical preconditions
or class invariants."
Seems they assume that something can detect coherency of all
input data before program is ran.

> So since std::out_of_range is a std::logic_error, I guess you're not
> supposed to use array::at() "speculatively".
>
> Possibly I missed some context.

No, you snipped it. ;) You wanted to see only first sentence and
not to read the context. See:

> [snip]

"But how can std::stoi() know that the out of range or illegal
input is coming from corrupt input data? Also for preventing
the inconsistency its caller has possibly to do most of what
stoi does itself."

The std::out_of_range and std::invalid_argument are other
std::logic_errors that std::vector does not throw but
std::stoi throws. And indeed these are guarding logical
preconditions that the text in the string is really number
and that it does fit into int. However converting strings
whose contents I know before program run into ints run-time
using std::stoi feels even worse logic error. I can instead
write those ints directly into code.

Keith Thompson

unread,
Oct 16, 2019, 6:35:03 PM10/16/19
to
Paavo Helde <myfir...@osa.pri.ee> writes:
> On 16.10.2019 0:59, Soviet_Mario wrote:
>> One thing I really can't stand is the way C used to share a single
>> variable (errno) to store status, that may be overwritten unpredictably
>
> It's not a single variable, it's a single variable per thread.

Right. The standard says errno is a macro "which expands to a
modifiable lvalue that has type int and thread local storage duration,
the value of which is set to a positive error number by several library
functions".

On my system, the errno macro expands to
(*__errno_location ())
where the __errno_location function presumably returns a pointer to a
thread-local int object.

This was introduced in C11. In C90 and C99, thread local storage did
not exist, but implementations could do something equivalent.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

The Doctor

unread,
Oct 16, 2019, 8:16:06 PM10/16/19
to
In article <05047d4c-6f30-4c4a...@googlegroups.com>,
ร รถ Tiib <oot...@hot.ee> wrote:

57 lines no content.
--
Member - Liberal International This is doctor@@nl2k.ab.ca Ici doctor@@nl2k.ab.ca
Yahweh, Queen & country!Never Satan President Republic!Beware AntiChrist rising!
https://www.empire.kred/ROOTNK?t=94a1f39b Look at Psalms 14 and 53 on Atheism
Canada - Choose Forward on 21 Oct 2019 !

Mike Terry

unread,
Oct 16, 2019, 10:31:43 PM10/16/19
to
On 17/10/2019 01:15, The Doctor wrote:
> In article <05047d4c-6f30-4c4a...@googlegroups.com>,
> � ö Tiib <oot...@hot.ee> wrote:
>
> 57 lines no content.
>

There seems to be a general problem the last several hours - I've seen
dozens of "no content" posts. They all seem to be Google Groups
posters, but possibly it's also related to my newsgroup servre (my ISP
uses GigaNews). The original messages are there on Google Groups..

Mike.

Ian Collins

unread,
Oct 16, 2019, 10:35:26 PM10/16/19
to
Must be a GigaNews problem, everything is fine on news.individual.net.

--
Ian.

Daniel

unread,
Oct 16, 2019, 11:06:30 PM10/16/19
to
On Wednesday, October 16, 2019 at 5:00:47 PM UTC-4, David Brown wrote:

>
> Correct me if I am wrong, but for the most part the only exception that
> will be thrown by the standard library is for failed memory allocations.

I don't think you can count on that. One example, implementations of
basic_streambuf::overflow are allowed to throw on failure.

Daniel


David Brown

unread,
Oct 17, 2019, 4:15:11 AM10/17/19
to
Yes, I see what you mean. From the other viewpoint, it means you get
the advantages and safety of RAII without having to use exceptions -
RAII classes are still an excellent way to structure a lot of code. But
again, with early returns you have more control - you can decide how
early to return, meaning you don't have to have make the parts
self-managing with RAII if it doesn't make sense in the program
structure. (And by "early returns", that includes any kind of failure
handling through return values.)

>>>> The other problem is that a lot of the information about C++ exceptions
>>>> that a function can throw, is basically lying.  You can /say/ that a
>>>> function only throws specific exception types, which would be very
>>>> useful, but it's a lie.  At least C++11 "noexcept" is a stronger, and
>>>> now "throw" specifications are no longer part of C++17 at all.  But
>>>> there is currently no way to say what exceptions a function really
>>>> could
>>>> throw, no way to check such features at compile time, no integration
>>>> with the type system, and the default is for functions to be specified
>>>> as "this function could throw anything, or pass on any exception"
>>>> rather
>>>> than the more sensible "this function won't throw - it will do what it
>>>> says it will do".
>>>
>>> This is true, but we are already there when we use the standard library
>>> unless we build with exceptions disabled.
>>>
>>
>> Correct me if I am wrong, but for the most part the only exception that
>> will be thrown by the standard library is for failed memory allocations.
>
> That is true, but even innocuous operations such as insertions can throw
> if a constructor of the type being inserted throws.  That's why I said
> we have to build (every component of an application) with exceptions
> disabled to truly grantee something won't throw.
>

If you are not throwing exceptions in your own classes, then insertions
won't throw, because constructors won't throw.

Of course, whether you can build the whole application with exceptions
disabled, and whether that is a good idea, is going to depend on the
type of application and the mix of code you have.

David Brown

unread,
Oct 17, 2019, 4:17:25 AM10/17/19
to
I'm getting the blank messages on news.eternal-september.org also.
Maybe they get their feed from GigaNews, or maybe the problem is wider.


Öö Tiib

unread,
Oct 17, 2019, 9:34:22 AM10/17/19
to
On Wednesday, 16 October 2019 17:12:40 UTC+3, David Brown wrote:
> On 16/10/2019 14:54, Öö Tiib wrote:
>
> I can try and give an outline of an example. Suppose you have code like
> this, using a bool "success" return instead of exceptions:
>
> bool success = tryThis();
> doThisAnyway();
> if (!success) return false;
> continueProcessing();
> return true;
>
> How do you make that into an exception-based handling, with "tryThis"
> throwing an exception on failure?

Thanks. I will try to explain how I would approach it. Not sure if my post
will show up though if there is some GG error.

First, the doThisAnyway() feels a bit like some close/release/erase/reset
kind of clean-up-after-work function. It is preferable to bind those into
destructors together with RAII like one of your examples did.

Also if the tryThis() is of "cleanup" kind (usually such calls are done
together) then it preferably should not throw in C++. Things that
may be called in destructors should indicate failures by other means
in C++ since standard library expects that destructors, operator
deletes and swaps don't throw.

If neither is cleanup function then it feels unusual. What it is that is
reasonable to do anyway regardless if previous steps failed? I can
live with duplicate code or with keeping the tryThis() non-throwing
or with making indeed some destructor-based "defer" for calling
doThisAnyway() but ... the whole situation feels interesting.

> With explicit, manual control of the errors or unusual circumstances, it
> can be easier to see the flow of the code.

It can make the actual algorithm harder to read. We need to add failed
state to all objects whose constructors or operators may fail. Checking
those is code. The actual reasons of failure are often abstracted
away by such "failed" state. A template can't be even expected
to know all failures that the objects it manipulates can face, so how
it can pass the information up intact? Exceptions are harder to
miss and can carry more information.

> I am not saying that exceptions /always/ make code harder to read - far
> from it. I am merely saying that /sometimes/ they do.

There I agree, I have seen very lot of code that was made bad with
exception handling. The reason is usually lack of skills, strategies
and policies. People should read the 31 suggestions from core
guidelines:
<http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-errors>
as minimum before using exceptions.

> (To be clear here, my kind of C++ programming is usually done with
> exception support disabled in the compiler - partly for worst-case
> performance reasons, partly for clarity, partly due to limited support
> for some targets, partly due to a mix of C and C++ code. This limits my
> experience of C++ exceptions.)

I understand it so that in embedded development there it is usually
just not enough power to pass sophisticated error handling
information around and also the total number of possible fatal
failures is so low that each can have personalized bit for it
reserved in some int (or couple).

> > So there have to be tools and policies in place for to use the exceptions
> > regardless of language. However it is not in any way better with checking
> > error codes of function "success" returns. Latter just adds lot of
> > required boilerplate code. Isn't that major readability cost?
>
> Somewhere there is /always/ going to be a cost!
>
> I think a lot of the readability cost of using function return values
> for errors comes from the days of older C and older C compilers, where
> you often had a mess of goto's in order to deal with errors underway
> without having deeply nested "if" statements. Often you can get a lot
> clearer code by simply dividing the code into several smaller functions,
> with a style of "if (!success) return false;" as an exit. Before C99
> "inline", and before compilers inlined single-use static functions
> automatically, multiple small functions could quickly be costly in
> efficiency.
>
> And of course with C++ you have all the power of RAII, and tools like
> std::unique_ptr - you don't need exceptions to use them. They apply
> equally well to exiting functions with an early return.

When failure means that caller has anyway not much to do
with more information then early false is fine and then the code
can be ok. However the error-handling policies often require
that way more information (potentially dynamic and structured)
is passed up about nature of failure.

David Brown

unread,
Oct 17, 2019, 10:34:30 AM10/17/19
to
On 17/10/2019 15:34, Öö Tiib wrote:
> On Wednesday, 16 October 2019 17:12:40 UTC+3, David Brown wrote:
>> On 16/10/2019 14:54, Öö Tiib wrote:
>>
>> I can try and give an outline of an example. Suppose you have code like
>> this, using a bool "success" return instead of exceptions:
>>
>> bool success = tryThis();
>> doThisAnyway();
>> if (!success) return false;
>> continueProcessing();
>> return true;
>>
>> How do you make that into an exception-based handling, with "tryThis"
>> throwing an exception on failure?
>
> Thanks. I will try to explain how I would approach it. Not sure if my post
> will show up though if there is some GG error.
>

It showed up for me, anyway. And that's all that matters :-)

> First, the doThisAnyway() feels a bit like some close/release/erase/reset
> kind of clean-up-after-work function. It is preferable to bind those into
> destructors together with RAII like one of your examples did.

If it is a pure cleanup, then RAII is likely to be the best way. But
maybe it is not pure - perhaps it is using some data that is local in
the function, or maybe it is a debug/log function that you don't want to
be part of a "tryThis" object. I am quite happy to accept that often
any kind of "doThisAnyway" code would naturally fit in an RAII
destructor - we are considering the situation where for some reason, it
is more natural, logical or maintainable to have it inside the main code.

>
> Also if the tryThis() is of "cleanup" kind (usually such calls are done
> together) then it preferably should not throw in C++. Things that
> may be called in destructors should indicate failures by other means
> in C++ since standard library expects that destructors, operator
> deletes and swaps don't throw.
>
> If neither is cleanup function then it feels unusual. What it is that is
> reasonable to do anyway regardless if previous steps failed?

Perhaps informing some other part of the system (or the user) of the
progress?

> I can
> live with duplicate code or with keeping the tryThis() non-throwing
> or with making indeed some destructor-based "defer" for calling
> doThisAnyway() but ... the whole situation feels interesting.

Yes, sometimes duplicate code is the simplest and clearest solution.

>
>> With explicit, manual control of the errors or unusual circumstances, it
>> can be easier to see the flow of the code.
>
> It can make the actual algorithm harder to read. We need to add failed
> state to all objects whose constructors or operators may fail. Checking
> those is code. The actual reasons of failure are often abstracted
> away by such "failed" state. A template can't be even expected
> to know all failures that the objects it manipulates can face, so how
> it can pass the information up intact? Exceptions are harder to
> miss and can carry more information.

A "success bool" carries all the information it needs (if a pass/fail
indication is all that is needed), and won't be missed. Optionals,
variants, expecteds, are all other related solutions with more
flexibility. And yes, templates /can/ be made to handle these.

>
>> I am not saying that exceptions /always/ make code harder to read - far
>> from it. I am merely saying that /sometimes/ they do.
>
> There I agree, I have seen very lot of code that was made bad with
> exception handling. The reason is usually lack of skills, strategies
> and policies. People should read the 31 suggestions from core
> guidelines:
> <http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-errors>
> as minimum before using exceptions.
>
>> (To be clear here, my kind of C++ programming is usually done with
>> exception support disabled in the compiler - partly for worst-case
>> performance reasons, partly for clarity, partly due to limited support
>> for some targets, partly due to a mix of C and C++ code. This limits my
>> experience of C++ exceptions.)
>
> I understand it so that in embedded development there it is usually
> just not enough power to pass sophisticated error handling
> information around and also the total number of possible fatal
> failures is so low that each can have personalized bit for it
> reserved in some int (or couple).

Yes. Modern microcontrollers are often big enough and powerful enough
to handle exceptions, but supporting exceptions can be costly. It
rarely matters on a PC if unwind tables adds 30% (taking a rough figure
out of the air) to the code space - probably it's still a drop in the
ocean compared to the graphics files and other non-code data in the
application, and your computer has almost unlimited disk space anyway.
30% bigger code space is a lot more significant when you have 256 KB of
flash.

There are many other reasons too. Often in embedded systems, you are
concerned about worst-case performance - the last thing you want is a
code path that takes a long and unpredictable time. You might not want
dynamic memory at all (or at least, you need to minimise it, or control
it with dedicated memory pools) - an exception system with unknown
numbers of exceptions floating around is not appropriate. (Again,
static exceptions might fix this.)

And often you simply design your functions so that they cannot fail - if
failure is not an option, you don't need exceptions.

Bonita Montero

unread,
Oct 17, 2019, 3:22:45 PM10/17/19
to
> They are a bad idea for normal processing as an alternative to return
> values.

When you know that the exception would always address the caller, you
can stick with return-values anyway.

Bonita Montero

unread,
Oct 18, 2019, 3:46:06 AM10/18/19
to
> The two key problems, as I see it, are first that exceptions means all
> sorts of code (function calls and expressions) can fail unexpectedly at
> any time, leading to early returns from your function. You have to be
> thinking all the time about what will happen if there is an exception
> when the code runs. This can mean making more RAII classes, or more
> indirect references (via unique_ptr, for example), simply to ensure
> cleanup in case functions throw an exception. That will make the code
> safe and correct - but can be of significant readability cost.

And having the cleanup mannually is more readable???

Jorgen Grahn

unread,
Oct 20, 2019, 6:18:14 PM10/20/19
to
I.e. bugs.

> Seems they assume that something can detect coherency of all
> input data before program is ran.

But there is no such thing. The way I read this thread (after
snipping, reconstruction and a weekend) std::logic_error is for bugs,
and std::stoi() misuses it. And that's also what you argue. Right?

>> So since std::out_of_range is a std::logic_error, I guess you're not
>> supposed to use array::at() "speculatively".
>>
>> Possibly I missed some context.
>
> No, you snipped it. ;) You wanted to see only first sentence and
> not to read the context. See:
>
>> [snip]

Sorry. What happened was I was too tired to read it all (and to read
the std::stoi documentation), but not too tired to read the sentence I
commented on and believed that, seen in isolation, I didn't agree with
it. So I complained about that isolated part, added the disclaimer,
and snipped because I didn't want to imply I had read all of it.

> "But how can std::stoi() know that the out of range or illegal
> input is coming from corrupt input data? Also for preventing
> the inconsistency its caller has possibly to do most of what
> stoi does itself."

Ok, so std::stoi() is new with C++11, tries to grab an int from a
string, and (among other things) throws std::out_of_range if the
string's number doesn't fit in an int.

That does seem a bit useless.

> The std::out_of_range and std::invalid_argument are other
> std::logic_errors that std::vector does not throw but
> std::stoi throws. And indeed these are guarding logical
> preconditions that the text in the string is really number
> and that it does fit into int. However converting strings
> whose contents I know before program run into ints run-time
> using std::stoi feels even worse logic error. I can instead
> write those ints directly into code.

I agree. I haven't used std::stoi, but I imagine if I /did/ use it
I would have to guard individual calls in try...catch, which to me
misses the point with exceptions.

Öö Tiib

unread,
Oct 21, 2019, 7:06:18 AM10/21/19
to
Somehow the preconditions are violated. Can be bug in this
program, can be data in file got corrupted, can be a person
prepared it wrongly manually or can be other, external program
made it wrongly.

> > Seems they assume that something can detect coherency of all
> > input data before program is ran.
>
> But there is no such thing. The way I read this thread (after
> snipping, reconstruction and a weekend) std::logic_error is for bugs,
> and std::stoi() misuses it. And that's also what you argue. Right?

If logic_error means only programming bug then yes, but it seems that
it is not the case. It is wider in standard so it seems to be intent. For
example we have similar logic_errors (invalid_argument) thrown by
std::bitset constructors (from string or char*) and several other places.

When there is code that does std::map::find, then compares it with
std::map::end and if equal then throws something otherwise continues
using found_iterator->second then why we may not use std::map::at
instead that does same and we get reference not that ->second bloat?

> >> So since std::out_of_range is a std::logic_error, I guess you're not
> >> supposed to use array::at() "speculatively".
> >>
> >> Possibly I missed some context.
> >
> > No, you snipped it. ;) You wanted to see only first sentence and
> > not to read the context. See:
> >
> >> [snip]
>
> Sorry. What happened was I was too tired to read it all (and to read
> the std::stoi documentation), but not too tired to read the sentence I
> commented on and believed that, seen in isolation, I didn't agree with
> it. So I complained about that isolated part, added the disclaimer,
> and snipped because I didn't want to imply I had read all of it.

No problem.

> > "But how can std::stoi() know that the out of range or illegal
> > input is coming from corrupt input data? Also for preventing
> > the inconsistency its caller has possibly to do most of what
> > stoi does itself."
>
> Ok, so std::stoi() is new with C++11, tries to grab an int from a
> string, and (among other things) throws std::out_of_range if the
> string's number doesn't fit in an int.
>
> That does seem a bit useless.

It is useful in sense that it is never thrown when the number fits into
int. When it is thrown then it depends on context how we should
react. If the string came from configuration of program then we
likely should refuse to run the program (as misconfigured). If it did
come from network request then we perhaps should reject only the
request (with some error code like 400).

> > The std::out_of_range and std::invalid_argument are other
> > std::logic_errors that std::vector does not throw but
> > std::stoi throws. And indeed these are guarding logical
> > preconditions that the text in the string is really number
> > and that it does fit into int. However converting strings
> > whose contents I know before program run into ints run-time
> > using std::stoi feels even worse logic error. I can instead
> > write those ints directly into code.
>
> I agree. I haven't used std::stoi, but I imagine if I /did/ use it
> I would have to guard individual calls in try...catch, which to me
> misses the point with exceptions.

That seems to depend on context. For examples that I gave
already above it may mean that configuration file of program is
defective or that single external request is defective.
What can immediate caller do to handle the situation? Take
some "default" int value? That is likely transgressing over its
responsibilities. Maybe I misunderstand your point.
On most cases we should let exceptions to propagate to level that
started the whole operation that now apparently failed (like reading
configuration or parsing incoming request).

0 new messages