> Therefore, there are many more places where an error return report would need to bubble up through, and that would cripple performance to handle the naive way, without exceptions.
Do you have any evidence for these claims? If a called function is quite long by itself, then there is hardly any added overhead if the client checks the returned code and that check would be "redundant".
For example, if I make a syscall and check the return value, there will be a duplicated checking effort with the kernel code in a way - but compared to the cost of calling into the kernel that overhead is a negligible cost for the benefit of modularization (kernel vs userspace application). This overhead will be very close to unmeasureable, and does not justify the introduction of exceptions which are an additional mechanism to return values which introduce syntactic and binary incompatibilities (i.e. non-orthogonal functionality).
Oh, by the way - wouldn't exceptions and stack unwinding have to do just as much work per stack frame? It can't really just skip over all frames in a single instruction.
I think the best solution to cruft is generally to not have it in the first place - instead of introducing a mechanism to skip over layers upon layers of "abstraction", it's better to just not have these layers. There should not be a large call chain that unwraps multiple stack frames in a row. The best code is code that just does what needs to be done in a straightforward way without using any library cruft. Nothing to "optimize" away this way.
Give any concrete example where exceptions are useful in order to work around cruft, and we can see if there is a better way to write it that does not require exceptions.
Frankly it seems hilarious to me that we're having a discussion about optimizing a few if's because they allegedly add too much overhead for the work of skipping over layers of cruft.
In the past years I have seen the need to use longjmp() (which is in a way a mechanism to get exceptions for C) only one single time, when I was coding an interpreter that got embedded into a longer running application. I didn't know how to skip through recursive layers of parsing calls in the recursive descent parser. But I still feel like there is a better way to write the parser (like, pushing work to parse to a data structure instead representing it as a nested stack frame). This could also benefit error reporting for example.
What you are calling "layers of cruft", other people call abstraction. That abstraction enables people to have code exactly tailored for a specific use without writing it over and over, slightly changed each time, for each use.
In C, of course, you have no choice; you write the whole function over and over. C programs are, e.g., filled with custom one-off hash tables, because you can't write a performant, general hash table library in C. People using C++ and Rust do not code their own hash tables, because they cannot match the performance of well-tested and tuned library components.
As a consequence, modern C++ and Rust coders get better-than-C performance for a lot less work, and ship with many fewer bugs, by relying on mature, well-tuned libraries. That is worth a lot of what you call cruft.
That said, it is not uncommon to find C++ and Rust coders aping what they see in general-purpose libraries by adding superfluous cruft in their own programs, that provides no such benefit. But that is a complaint for another day.
> wouldn't exceptions and stack unwinding have to do just as much work per stack frame? It can't really just skip over all frames in a single instruction.
Taking that back. I forgot for a second that we're focusing on the cases where the exception is not thrown.
Do you have any evidence for these claims? If a called function is quite long by itself, then there is hardly any added overhead if the client checks the returned code and that check would be "redundant".
For example, if I make a syscall and check the return value, there will be a duplicated checking effort with the kernel code in a way - but compared to the cost of calling into the kernel that overhead is a negligible cost for the benefit of modularization (kernel vs userspace application). This overhead will be very close to unmeasureable, and does not justify the introduction of exceptions which are an additional mechanism to return values which introduce syntactic and binary incompatibilities (i.e. non-orthogonal functionality).
Oh, by the way - wouldn't exceptions and stack unwinding have to do just as much work per stack frame? It can't really just skip over all frames in a single instruction.
I think the best solution to cruft is generally to not have it in the first place - instead of introducing a mechanism to skip over layers upon layers of "abstraction", it's better to just not have these layers. There should not be a large call chain that unwraps multiple stack frames in a row. The best code is code that just does what needs to be done in a straightforward way without using any library cruft. Nothing to "optimize" away this way.
Give any concrete example where exceptions are useful in order to work around cruft, and we can see if there is a better way to write it that does not require exceptions.
Frankly it seems hilarious to me that we're having a discussion about optimizing a few if's because they allegedly add too much overhead for the work of skipping over layers of cruft.
In the past years I have seen the need to use longjmp() (which is in a way a mechanism to get exceptions for C) only one single time, when I was coding an interpreter that got embedded into a longer running application. I didn't know how to skip through recursive layers of parsing calls in the recursive descent parser. But I still feel like there is a better way to write the parser (like, pushing work to parse to a data structure instead representing it as a nested stack frame). This could also benefit error reporting for example.