Exceptions are Harmful?
Posted: January 28th, 2018, 4:48 am
Got an interesting comment on my latest vid on exceptions. Thought I would share my response here as well. You can find the full text of the original comment on the YouTube video.
My reply:
Yeah, a lot to unpack here. First, I agree that exceptions can be overused. They should be for runtime errors. Verifying that preconditions are met (design by contract stuff), doing sanity checks, etc. are the realm of assertions. Exceptions are for things outside of the programmer's control.
>> And isn't it reasonable that if your function returns an error code to the calling function that calling function can decide to progress down a different path instead of the "something went wrong path" (which now becomes common for all error cases, and to avoid that you need to type a lot of extra boiler plate code).
The calling function can decide to progress down a different path. You are not limited to just one error path. You can catch at different levels, so you can have one try for the whole logic, but you can also have a nested try around a single call if you want errors (or just specific types of errors) from that call to handled down a different code path. No "extra boiler plate" required, just replace your if condition and error handling block with a try...catch.
>> Exceptions bring in a lot of runtime overhead, and are very hard to design from a runtime standpoint, which could come around to byte you in the but when it becomes too late.
This is a common misconception that needs to be dispelled already. In all the major vendors, exceptions are implemented such that they have zero overhead for nothrow code paths, and this is achieved by sacrificing performance of the throw path. So exceptions are very expensive when thrown, essentially free when not. But it is naive to hear this and think "exceptions are slow!" Think about it, exceptions are the EXCEPTIONAL cases, they are the one in 10,000, one in a million. They do not affect performance during normal operation, so to avoid them for the sake of optimization is foolish to the extreme. And if your application is throwing enough exceptions per second to actually constitute a performance bottleneck, you've got a lot bigger problems on your hand. Like maybe your software is just actually broken :/
>>Not to mention that the return values from functions are of single type while exception can be of many types which allows for situations where your function implementation introduces overhead in a code of someone who's using your library.
Yes, exceptions are a more powerful construct than return values. You are free to throw different types, which can allow you leverage polymorphism to perform sophisticated error filtering. But just like any other tool, you are not forced to do so, you are merely given the option. If you are having trouble handling that aspect, just exercise some self discipline. After all, functions are also permitted to return any value, you must make the choice to have them all return the same type for error code purposes as well.
Although I said above that I exceptions can be overused, that does not mean that I advocate for return values. In actuality, most applications see little use for exceptions because they have no place in business logic, which is the meat of most systems. The runtime errors they are meant to address arise primarily in input/output operations (but also in memory allocation for out of memory errors). I see no use for error codes here, exceptions are superior. Look at all the popular languages developed in the last couple of decades; they all choose exceptions for primary error handling mechanisms. This is not a coincidence, nor is it a conspiracy. This is evolution.
On the other hand, satisfaction of preconditions etc. are handled by assertions. No need for error codes here either. There are really only 2 main scenarios for error codes: 1) you're writing a "C" linkage API. You obviously can't let your interface functions throw; and 2) you're working with (not interfacing with, but actually actively modifying) legacy code (exceptions are not something you want to bolt on after the fact).
I really love the C++ language. It gets a bad rap, some of it deserved maybe (complex) but a lot of it not so much (too low-level). Part of the reason for this is there are a lot of C programmers who claim to be C++ programmers, but who have never actually updated their skills to the modern era. And then they pass down those practices to the next generation and perpetuate the cycle all over again. It's time to evolve. Either that, or get left in the dust.
My reply:
Yeah, a lot to unpack here. First, I agree that exceptions can be overused. They should be for runtime errors. Verifying that preconditions are met (design by contract stuff), doing sanity checks, etc. are the realm of assertions. Exceptions are for things outside of the programmer's control.
>> And isn't it reasonable that if your function returns an error code to the calling function that calling function can decide to progress down a different path instead of the "something went wrong path" (which now becomes common for all error cases, and to avoid that you need to type a lot of extra boiler plate code).
The calling function can decide to progress down a different path. You are not limited to just one error path. You can catch at different levels, so you can have one try for the whole logic, but you can also have a nested try around a single call if you want errors (or just specific types of errors) from that call to handled down a different code path. No "extra boiler plate" required, just replace your if condition and error handling block with a try...catch.
>> Exceptions bring in a lot of runtime overhead, and are very hard to design from a runtime standpoint, which could come around to byte you in the but when it becomes too late.
This is a common misconception that needs to be dispelled already. In all the major vendors, exceptions are implemented such that they have zero overhead for nothrow code paths, and this is achieved by sacrificing performance of the throw path. So exceptions are very expensive when thrown, essentially free when not. But it is naive to hear this and think "exceptions are slow!" Think about it, exceptions are the EXCEPTIONAL cases, they are the one in 10,000, one in a million. They do not affect performance during normal operation, so to avoid them for the sake of optimization is foolish to the extreme. And if your application is throwing enough exceptions per second to actually constitute a performance bottleneck, you've got a lot bigger problems on your hand. Like maybe your software is just actually broken :/
>>Not to mention that the return values from functions are of single type while exception can be of many types which allows for situations where your function implementation introduces overhead in a code of someone who's using your library.
Yes, exceptions are a more powerful construct than return values. You are free to throw different types, which can allow you leverage polymorphism to perform sophisticated error filtering. But just like any other tool, you are not forced to do so, you are merely given the option. If you are having trouble handling that aspect, just exercise some self discipline. After all, functions are also permitted to return any value, you must make the choice to have them all return the same type for error code purposes as well.
Although I said above that I exceptions can be overused, that does not mean that I advocate for return values. In actuality, most applications see little use for exceptions because they have no place in business logic, which is the meat of most systems. The runtime errors they are meant to address arise primarily in input/output operations (but also in memory allocation for out of memory errors). I see no use for error codes here, exceptions are superior. Look at all the popular languages developed in the last couple of decades; they all choose exceptions for primary error handling mechanisms. This is not a coincidence, nor is it a conspiracy. This is evolution.
On the other hand, satisfaction of preconditions etc. are handled by assertions. No need for error codes here either. There are really only 2 main scenarios for error codes: 1) you're writing a "C" linkage API. You obviously can't let your interface functions throw; and 2) you're working with (not interfacing with, but actually actively modifying) legacy code (exceptions are not something you want to bolt on after the fact).
I really love the C++ language. It gets a bad rap, some of it deserved maybe (complex) but a lot of it not so much (too low-level). Part of the reason for this is there are a lot of C programmers who claim to be C++ programmers, but who have never actually updated their skills to the modern era. And then they pass down those practices to the next generation and perpetuate the cycle all over again. It's time to evolve. Either that, or get left in the dust.