Exceptions are Harmful?

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Exceptions are Harmful?

Post by chili » 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.
Chili

User avatar
DicheBach
Posts: 22
Joined: December 11th, 2017, 3:29 pm
Location: recurring

Re: Exceptions are Harmful?

Post by DicheBach » January 29th, 2018, 3:35 pm

An exceptional post . . . Ba dum tsh!

Having had a couple days worth of course work on exceptions, and finished a few homeworks, i.e., just a barebones understanding, the thing that puzzles me about them is:

It seems as if writing good exception handling code involves anticipating the unpredictable, and moreover, anticipating the unanticipated problems that arise as a result of problems in a complex code base, i.e., problems caused by the code itself . . . but . . . if you can anticipate the problems caused by the code itself, then why write "exception handling routines" to flag such problems, just fix the problems in the code that would allow exceptions to occur in the first place!?

I have no doubt my understanding remains quite rudimentary, but that is where I'm at.
The greatest joy a man can know is to conquer his enemies & drive them before him.
To ride their horses & take away their possessions. To see faces of those who were dear
bedewed with tears & clasp their wives & daughters to his arms.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Exceptions are Harmful?

Post by albinopapa » January 29th, 2018, 7:14 pm

I kind of see where you are coming from, but I view exceptions as something you cannot recover from.

What should happen when you are trying to create/save/copy a file and there is a write error to the hard drive? Do you crash your program? Is your program capable of looking for another location on the hard drive to write to?

What should happen when you run out of memory to allocate? You may be able to release some of your own resources, that's one path, but what if you need all those resources for the program to run?

In the framework, chili throws an exception of any of the D3D resource creation functions fail. As long as all the arguments are valid, then what alternative is there? In some of those, there could be feature level limitations, so that could be dealt with since it's not currently in the framework as his framework is set to use feature level 9.1 or higher. This means, you can have an older video card, but anything older than Windows Vista with service pack 1 or 2, would not be allowed. So his options are, throw an exception, or create an alternate path to use the DirectX 9 API instead.

When you can handle a situation that is not the fault of the program or programmer, then use if/else if/else. If all condition branches have been covered and you can't recover from the situation, then throw an exception stating what the problem was. As chili mentions in the video, exceptions are exceptional cases, I extend this to things you cannot recover from.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Exceptions are Harmful?

Post by chili » January 30th, 2018, 1:49 am

DicheBach wrote:anticipating the unanticipated problems that arise as a result of problems in a complex code base, i.e., problems caused by the code itself . . . but . . . if you can anticipate the problems caused by the code itself, then why write "exception handling routines" to flag such problems, just fix the problems in the code that would allow exceptions to occur in the first place!?
This is a good question. In fact, it's so good, that I have already addressed it in my video on exceptions :lol:

Exceptions are not about "anticipating the unanticipated", and they are definitely not about "problems that arise as a result of problems in a complex code base, i.e., problems caused by the code itself". They are about exceptional problems (issues not encountered during 'normal' operation) that arise during execution and that cannot be avoided by the programmer at compile time.

A great example that you should be able to understand is networking. During normal operation, you send and receive data and go about your processing. 99.9% of the time this will run smoothly (at least from the perspective of the application layer), but maybe one day the cat knocks over the modem and pulls the ethernet cable out. What happens then is the next time you call a networking function, it is going to result in an error.

You could respond in a number of ways. You could ignore the error, in which case you will likely get garbage input/your program will hang waiting for packets. You could handle the error, but how? Well, you might want to boot the user back to networking lobby when a connection is lost. But the error was likely detected many layers down, at a low networking code layer. This layer has no idea about lobbies or games; it has no way handle the error in a meaningful way. Even if it could, generally your networking layer is code that will be used in many different projects. Hard-coding your networking lib to return to a game lobby upon disconnection seems pretty dumb then the next time you try to use your lib for something like an ftp client.

What if you throw an exception? Do you think that might be a good solution to this problem? The networking library will simply signal an error and transfer control DIRECTLY to a (perhaps distant) routine that is in a better position to be able to handle it meaningfully. Now you can handle the error at any level you like, depending on the exact application and requirements. You can recover elegantly from this EXCEPTIONAL situation, and then return to normal program flow paths at the point of recovery.

For logic errors, bugs in your own code, exceptions are not preferred, as I mention in my video. You can and should solve these problems (ideally) at the development stage, with tools such as the debugger, TDD, etc. It is not a bad idea to add tests for logic errors as well (we call these 'sanity tests'), but use assertions for these. They signal intention, and they also have the benefit of not adding extra overhead to release builds.

There is a lot more that could be said, but alas my time is not infinite. Hopefully this clears some things up for you m8. ;)
Chili

User avatar
DicheBach
Posts: 22
Joined: December 11th, 2017, 3:29 pm
Location: recurring

Re: Exceptions are Harmful?

Post by DicheBach » January 30th, 2018, 2:51 pm

Very cool! I reckon when I get to your episode on Exceptions, it will become even more clear. Probably a significant portion of my limited comprehension comes from relying too much on the explanations of the single prof from whom I've received instruction so far (and being too lazy so far to delve into it father on my own).

My Ph.D. in computer security instructor might have been really good at finding evidence of pedo porn on suspects machines, but she wasn't quite so good at explaining this sort of stuff ;)

As always I appreciate and greatly value your instruction Chili. You seriously should be chair of a computer science program somewhere and set those suckers straight on how to educate!
The greatest joy a man can know is to conquer his enemies & drive them before him.
To ride their horses & take away their possessions. To see faces of those who were dear
bedewed with tears & clasp their wives & daughters to his arms.

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Exceptions are Harmful?

Post by chili » January 31st, 2018, 1:56 pm

Cheers mate. I sorta assumed that you had already seen that video, which vexed me by 0.1% :lol:
Chili

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Exceptions are Harmful?

Post by albinopapa » February 6th, 2018, 7:35 pm

Ran across a situation where using exceptions as flow control was kind of necessary. The C library has a function double strtod(const char*, char**), which tries to interpret a string and return a double. The first parameter is the source c-string and the second parameter would be the address of the end of the string representation of the double. For example ( 1.57392 * ... ) the second param would be the address of the space between 2 and * if the string can be converted to a double. If not, then the string is read to the end and the address is the same as the source string.

If you use the C++ version double std::stod( const std::string&, size_t* ), which is just a wrapper for strtod, you are able to pass in a std::string and when the function returns, you get the double as the return parameter and the offset from the beginning of the source string signifying the end of the double string representation. Being C++ this wrapper throws exceptions if the source std::string does not convert to double.

That was the setup.

The project I'm working on is going to parse text into tokens. These tokens can be words, symbols and numbers ( double, float or integer ). If I try checking a token to see if it is convertible to a number by passing it to this C++ wrapper, I get an exception thrown if it is not a convertible string. In this case, I can't just use if/else because of how exceptions work. I of course don't want the program to end, so I have to use a try/catch block instead if I want to resume searching through the text.

Code: Select all

double ToDouble( const std::string& buffer, size_t& Idx )
{
	try
	{
		return std::stod( buffer, &Idx );
	}
	catch( const std::exception& )
	{
		Idx = -1;
		return std::numeric_limits<double>::infinity();
	}
}
To be fair, this is probably not the best way to do something like this, but this function is modified from a git repo I am using as a reference for my own project.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Exceptions are Harmful?

Post by chili » February 7th, 2018, 3:10 pm

I used exceptions pretty extensively in the error handling for the assembler I wrote for the Chili Pork Unit, and I'm pretty satisfied with the way that turned out. I don't hate the above solution either, though a non-exception alternative would also be appreciated I think. You can do it without exceptions by using a stringstream and checking the fail bit, but it's a little roundabout.
Chili

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Exceptions are Harmful?

Post by albinopapa » February 8th, 2018, 1:26 am

Yeah, this repo is a mixture of C and C++ code. Since it is a parser, instead of using string::iterator, they just used char*. The rules for const pointers is different than with const references apparently, or maybe a bug in VS. There is a struct with a const Member* with members of it's own ( struct in a struct ). You can change the members of a const Member* even though it's suppose to be const.

It's something like

Code: Select all

struct SomeOtherNode
{
     T member;
};
struct SomeNode
{
     const SomeOtherNode* psoNode;
};
// Seems to be valid
SomeNode *psNode;
psNode->psoNode->member = some_data;
even though psoNode is a const T*.
Changing this to a const T& has the expected behavior of propagating to all members.

Sorry, off point. I don't like the implementation they used, const char*, char* and char[] used for buffers, strings and iterators. All very confusing. Then the use of the C api including va_args b.s. bleh.

One thing I did notice about using const char* though is a lot of stuff could be compile time constants. So their implementation is probably a lot faster instead of using std::string with iterators. Even with const string& and move semantics it would be hard to beat.

I agree, using std::stringstream would have been a better choice here. Honestly, not sure why the C++ version would throw. It already has an inout param, so just setting the index to -1 or std::string::npos ( same as -1 ) would have been sufficient, plus that's how they handle end of stream stuff in the rest of the string api anyway.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply