Re: Oozing poison
On 01/29/12 11:49 AM, Scott Lurndal wrote:
Ian Collins<ian-news@hotmail.com> writes:
On 01/28/12 05:18 PM, Scott Lurndal wrote:
However, in this case (modeling a physical device), the best determination
of how to present the failure can be made as close to the cause of the
exception as possible. I would think that for most applications that
can actually recover from an error (as opposed to just catching std::exception
at the top level, printing a message, and exiting), keeping the recovery action
as close to the code that actually failed makes recovery much simpler.
It may well be, but the catch and the throw my still be several calls
away. Using nested small functions is much cleaner with exceptions than
with return codes. In your example you return the state in the object
passed. An exception based design may well pass the state in an
application specific exception.
Which will have additional overhead to allocate, construct,
destroy and deallocate the application class derived from std::exception, nicht wahr?
Only in the exceptional circumstances where one is thrown. As I said,
the state has to be returned somewhere.
I think you would find that "small nested functions" is exactly what is
used, and in this case, the object passed doesn't necessary _return_ the state
passed so much as provide a channel for returning the state to the final
receipient.
Now in the core processor simulation code sigsetjmp/siglongjmp are used to reset
the simulated processor to the start of the instruction processing loop when
an exception occurs executing an instruction (an address error, invalid instruction,
etc). These could possibly be converted to exceptions, but I'm quite sure that
the performance would be worse and the RAS characteristics of the application
wouldn't change appreciably.
By using exceptions, you are able to utilise the full power of the
language, such as RAII which out of band jumps preclude.
One point I have been trying to make is bolting exceptions onto an
existing design is seldom a good idea. Neither is having to take them
out, which was a situation I once found myself in! Error handling is a
fundamental part of a system's design and mixing paradigms invariably
ends in tears.
I remember reading Tom Cargill's article on exceptions back when they were
introduced to the language - I think many of his points still apply - exceptions
are not a universal panacea and careful analysis of all codepaths need be done
to prevent memory leaks or access to deallocated data when exceptions are being
thrown.
This is true and the language provides us with the tools to achieve
this. Tools which cannot be used with out of band jumps!
The same caveats, of course, apply to setjmp/longjmp as well, but I
don't need to allocate an exception object (just set one of 10 bits in a member
field of the current 'this' when the longjmp is issued; the jmp_buf also a member
of the current object).
The cost of constructing an exception object are far outweighed by the
cost of throwing it *in the exceptional case*.
The catch is also the reason for all the extra code, constructing and
destructing a temporary std::exception object. The actual exception
handling part of the code is this bit:
Yet creating and destroying a temporary std::exception object also counts in
terms of both extra cycles and code footprint. Both of which impact
performance (generally negatively).
Only if the exceptional condition occurs. The normal path of execution
will not be impacted (and will be cleaner and faster than the error
checking case).
I haven't seen this in practice, but then my sole exposure to C++ code
has been in the Operating Systems/Microkernel/Hypervisor area none of which
used exceptions (most of which predated exceptions :-), aside from one
application at a major internet certification authority, which also predated
exceptions.
That's where our experiences diverge, I've been comparing the
performance of exceptions on various platforms for many years and I've
found exceptions are generally faster (always on the Unix and Linux
platforms I use). From my perspective, the exceptions vs error returns
argument was settled long ago.
Let me be clear, I never actually took an exception in the test I described above;
the sole difference was changing the
buf = malloc(XXX); if (buf == NULL) { YYY}
test to
try {buf = new uint8[XXX];} catch (std::exception& e) { YYY }
(and of course, the corresponding 'free()'<-> 'delete []' changes).
You have introduced two variables, exceptions and new. Try eliminating
both.
--
Ian Collins