On 2021-09-03 4:23 p.m., Adrien Destugues wrote:
> 3 septembre 2021 22:07 "Alexander G. M. Smith" <agmsmith@xxxxxx> a écrit:
>> On 2021-09-03 3:47 p.m., Alexander G. M. Smith wrote:
>>> If you want to turn exceptions inside-out, make the error object a
>> stack. Each level that wishes to can add a string explaining why it
>> failed. So you can drill down to see what happened.
>>
>> On further thought, using a second area of memory to store the stack
>> isn't that complex. It's always being written to, not edited, so you
>> can sequentially write the strings, error codes and debug info into
>> memory. Don't even bother indexing it, just linearly search through it
>> to find a particular entry. There shouldn't be too many, well, except
>> in a recursive crash (change it into a queue when it gets too big and
>> forget the deeper entries?).
>>
>> If it needs to be multitasking safe, use fixed size memory blocks and a
>> free list and a semaphore. Link in another block when you need more
>> space to append the data. Linearly searching through the error stack
>> now requires a jump once in a while, but overall speed is still O(n*n)
>> to find a particular stack entry. Not great, but good enough.
>
> Nothing is impossible, but that is a lot of effort and complexity to
> essentially do "exceptions without actually using exceptions".
It's partly like exceptions, but with user readable strings only at
interesting levels of callback. Though now that you mention it, you
could do the same with exceptions, if you had a stack-of-strings
exception object and remembered to append an error message string to it
at each level of interest.
That would be exceptions with a strict discipline on using them.
So, how are exceptions actually implemented? Google time...
https://www.codeproject.com/Articles/2126/How-a-C-compiler-implements-exception-handling
Each function call has an exception record inside its stack frame,
specifying a handler subroutine (created by the compiler for each
function), some sizes and a next pointer. An OS pointer at FS:[0] (one
per thread) is used to add the record to the list of exception handlers
in the function prologue. There's also data about catch blocks used by
the function, stored somewhere else just once and set up by the
compiler. And an id number that identifies which try block is active
(updated as the function runs, by code inserted by the compiler). There
are also id numbers that identify which variables are active.
On an exception, the list of exception records is tested starting at
FS:[0] until one of the handlers says it wants to handle that kind of
exception (catch is based on the data type of the exception object).
Then the stack needs to be unwound, by calling all the handlers before
the one which can do the exception, telling each one to unwind its
particular stack (calling destructors mostly, for variables that are in
scope, however it knows that - there's a whole other section about that
topic). Then the exception object is copied to the frame of the
function that's catching the exception. Then it can run the user catch
code, then clean up the exception object and stack, and maybe throw an
exception again.
Nah, I think my stack of strings is simpler. :-)
But however you do it, I'd like a way of getting multiple levels of
readable error messages to the user. I expect they would initially just
see the top one and can drill down to see the details. A single error
code isn't enough. A single readable string is better (one which
includes related values - such as which database record was being
processed). But multiple levels of failure explanation is best.
- Alex