On Thursday, 25 August 2016 02:17:36 UTC+3, David Brown wrote:
> On 24/08/16 19:35, Öö Tiib wrote:
> > On Wednesday, 24 August 2016 14:14:15 UTC+3, David Brown wrote:
>
> >>
> >> /If/ you are in a position to use exceptions well, then there are
> >> advantages in using them. But that means that /you/, and all the other
> >> programmers on your team, need to understand them and use them
> >> appropriately. It also means that any libraries or third-party code you
> >> use must be equally good.
> >
> > So part of exception handling skill is how *not* to throw into legacy
> > code (module or library) that expects nothrow but does not constrain
> > it in interface or how to edit the interface to constrain it.
> >
> > About unskilled people it is anyway gamble. Can't handle exceptions?
> > Can they manage resources? Resources besides memory? Can they
> > handle threads and locks? Etc. C++ without exceptions is no way
> > simple and foolproof tool.
>
> It is neither simple nor foolproof, whether or not you use exceptions.
> Exceptions are just one tool in the C++ toolbox.
So my solution with that so far has been to give the tool to people
who can handle it or at least express excitement to learn to. Existence
of people who can't handle it and/or dislike it does not matter.
>
> >
> >> And since "exception safe" can mean many
> >> different things ("basic" guarantee, "strong" guarantee, "nothrow"
> >> guarantee) you have to make sure you have everything coordinated
> >> appropriately.
> >
> > These guarantees are trivial and obvious. Let me list?
> >
> > * The "nothrow" guarantee is marked with "noexcept" specifier in C++.
> > Exceptions won't be ever thrown out of such functions. How it can be
> > simpler?
>
> Note that "noexcept" is from C++11 onwards. The older exception
> specifications did not guarantee anything (other than giving the
> compiler new ways to make things go wrong for your program if you don't
> follow your specifications).
Yes. In C++98 the exceptions were somewhat worse. It is generally considered
good news that things evolve.
>
> But since noexcept (like throw()) is not checked at compile time, it
> means manual work - you have to figure out if your function really does
> not throw anything, which means figuring out if any of the operators,
> function calls, etc., in the function are definitely noexcept.
>
> Sure, it can be done - but the effort involved is not negligible. If
> the end result is better, safer code, then great. If not, the effort is
> wasted, and it's another thing for people to get wrong.
Yes. Exceptions like everything else are not silver bullet. There is cost
when we code and run-time cost. Misuse may result with inefficiency
*and* more work. So don't misuse, use. ;-)
For example hard drive can become full. For what operations in our
program it is showstopper issue? Where we discover it? Where we
can do anything about it? How likely it is that it happens? Answers
to those questions likely show that it is perfect candidate to handle
with exception but is sure not as "cheap" as to ignore the whole
possibility and to assume that hard drive is *never* full. I remember
Windows NT 3.5 was such that just died down when system drive was
full.
>
> >
> > * "Basic" guarantee is that all objects will be in valid invariant and there
> > are no resource leaks when exception is thrown out of function. That
> > very guarantee is expected *always* in C++ regardless if function does
> > throw or not. So it is only essential and is achievable with RAII alone
> > on case of exceptions.
> >
> > * "Strong" guarantee means that function rolls everything back into
> > state before call if it throws. That means special design effort is
> > used that can be expensive. That design effort however does not
> > differ from a function that has no side effects (rolls everything back)
> > when it returns an error code. So it is special *functionality* and is
> > expected to be documented about a function.
> >
>
> Having a strong guarantee on a function can definitely be a useful thing.
My point was that there has to be such requirement and that it is orthogonal
to exceptions. Example: In middle of overwriting a file we may discover that
we lack something to complete the operation. Now we want the file back how
it was before we started to write it. Rolling that file back makes sense
regardless if we use programming language that supports exceptions or not
and the programming effort and run-time cost of it is not somehow magically
bigger for languages that have exceptions.
>
> But consider what you are protecting against by this "special design
> effort". A sizeable percentage of programs are run on PCs, by humans.
> They will /never/ run out of memory, assuming you don't have leaks. The
> action on failure, from a general "catch" somewhere in or close to
> main(), will be to present an error message and then die. In such
> cases, all the extra effort in making strong guarantee functions is
> wasted. Worse than that - more code means more opportunities for bugs,
> and the complications for the strong guarantee can make code much
> slower. And all in the name of avoiding something that will never
> happen, and minimising the consequences unnecessarily.
I do not see what you mean. The strong roll-back guarantee is needed
only on very special cases IMHO. The basic exception guarantee is plenty
on general case.
>
> And there are lots of systems where errors are simply not acceptable -
> exceptions should not be thrown. Then there is not need to throw or
> catch exceptions - at best, they are a tool to help during testing and
> development.
>
> Again, I am not saying it is wrong to use exceptions, or wrong to
> provide strong guarantees - just that it comes with a cost that is often
> not worth paying.
Again I can't see your point. We can't redefine the universe that is full of
things that can break down or fill up or reach limits in various ways.
"Hard drive full" is inacceptable? "Sensor short circuited" is not acceptable?
Reality has to be acceptable, problems have to be acceptable. Solution of
quitting to work when sensor is short circuited may be unacceptable. That
is again a requirement orthogonal from if the event when sensor was
discovered being short circuited was propagated with exceptions or error
codes. On any case we have to behave differently with broken sensor and
there goes the real development effort.
... snip
>
> > Usage of low quality tools can result with unanticipated expenses regardless
> > if we use exceptions or not. It is same as with unskilled workforce, exceptions
> > are not *the* issue there and don't stand out as special, something else is
> > the issue.
>
> Lack of support for exceptions, or inefficient implementations, does not
> mean low quality tools.
Ok, former is better described as "non-conforming to specification" tool and
latter as "inefficient" tool. Both have "quality of implementation issues" IOW
are "low quality" tools. The quality of tool can be is not uniform so some other
feature is possibly made brilliantly there but we got fly in the ointment so "low
quality".
>
> >
> >> Another is that it can hinder optimisations
> >> (by enforcing ordering of operations, or pushing local objects onto the
> >> stack). And if you are trying to get a "strong" exception safe
> >> guarantee, you often need a good deal more code, and a good deal slower
> >> code. A function that modifies an object "x" with the strong guarantee
> >> will typically take a copy "x2" of "x", modify "x2" in a way that might
> >> thrown an exception, and then swap "x" and "x2" once everything is
> >> completed safely. That means an extra copy-constructor, and an extra
> >> destructor - that's hardly free if "x" is a large data structure.
> >
> > What you mean? We often have to make backup copies of objects when
> > failed attempt of operation may change it (but we guarantee not to)
> > regardless if we throw exception after we failed or we return error code.
> > What else you suggest to do on general case?
> >
>
> You may well have to make backup copies if you want to write
> trial-and-error code, whether or not you use exceptions. But when you
> have exceptions, you have to be aware that just about anything can fail
> unexpectedly with an exception. Without exceptions, and using explicit
> error handling, you know where things can go wrong.
What you mean by that "trial-and-error code"? Doing something may be the
cheapest and sometimes the only available proof that it is doable. We may
check that hard drive has plenty of room beforehand, but millisecond later
some other process uses it up and our write fails. Real code is actually full
of unhandled exceptional circumstances with or without exceptions. Lets
take first allfamous "trial-and-error" C program:
/* Hello World program */
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
printf("Hello World!\n");
return EXIT_SUCCESS;
}
The return value of 'printf' is unhandled and therefore 'EXIT_SUCCESS'
is a lie on such exceptional case when it fails. If 'printf' would throw
on failure then there at least would be chance that some static analysis
tool would report about potentially unhandled exception. Otherwise
all same old crap.
>
> >>
> >> There are, of course, a great many benefits to be had from using
> >> exceptions and writing exception-safe code. But you need to be clear
> >> that the benefits only come if the code really is exception safe, that
> >> it is realistic for exceptions to occur, and that you have something
> >> useful that you can do /if/ an exception occurs. C++ gives you the
> >> /option/ of using exceptions - it does not force you to do so, and it is
> >> not always the right choice.
> >
> > With that I agree. Also I feel that I have never claimed that usage of
> > exceptions is correct choice in every situation. Exceptions are good
> > only for to handle exceptional situations (like name indicates).
> > That does not mean that such handling itself is rare in code. In mature
> > program over half of handled situations are exceptional.
> >
>
> My dislike for exceptions is that they hide things that I would prefer
> to be out in the open.
>
> If I see a function:
>
> int foo(int x);
>
> I read that as a function that as a function that takes an integer "x",
> foo's it, and returns an integer result. It is clear and simple, and
> ready to use.
>
> But with exceptions, foo is now a function that takes an integer "x",
> and /either/ foo's it and returns an integer, /or/ it returns a
> completely unexpected object of an unknown type that may not even be
> known to the user of foo, which will lead to a jump to somewhere else in
> code that the user of foo doesn't know about.
Exceptions must be documented like any other potential outcome. If the
exceptions are potentially from injected stuff then that must be also
documented. Also some of it can be constrained and handled compile-
time. The 'noexcept' can be 'enable_if'd for and so constrained or
specialized for.
The 'throws' clutter in Java function's signature does not work well;
programmers weasel out of it by throwing covariants and it is
getting close to 'public void foo(String myString) throws Throwable'
plus actual exceptions described in documentation. So it is only
good how C++ deprecated that worthless nonsense bloat.
>
> If foo is a function that could fail, I prefer to know about it:
>
> tuple<int, bool> foo(int x);
I prefer 'boost::optional<int>' there when lack of return is normal
and reason of it is obvious and show must go on anyway. The
'pair<int,bool>' or 'tuple<int,bool>' are less convenient.
However if 'false' is exceptional (say 1/50000 likelihood) and it
breaks the whole operation during what that 'foo' was called then
handling it in close vicinity just pointlessly clutters the core logic
up.
With exceptions we can deal farther away or may be even up stack
in 'catch' block, and there we may also get the phone number of
grandmother whose fault it was that hard drive was full and no
'int' did emerge from 'foo' and so we could not complete the whole
operation.
>
>
> As an aside, I /really/ hope that structured binding makes it into
> C++17, so that we can write:
>
> auto [result, success] = foo(123);
That is another issue how to express what are the 'in', 'in-out' or 'out'
parameters. It is lot simpler since it is syntax issue. Exceptions are
not any of those (and may be rather long list) so it is good idea to
handle those separately.