The fact that disorder exists in the universe. The fact that hardware and software are made by humans and humans are imperfect. The fact that the input to our programs typically comes from humans and, again, humans are imperfect. This means that, sooner or later, something your code tries to do won't work, and if your code behaves as though something worked (such as opening a file for reading) when it actually didn't (user supplied a path to a non-existent file), at best nothing will happen (when your program tries to write to an uninitialised stream, its output will be discarded) and your user will have no clue as to why. So obviously some kind of error reporting (by the OS or libraries) and handling (by you) is necessary.
So I can't play the game with MyFavoritePlugin just because someone forgot to include a sound for bunnies?
Let's work with this one. So now instead of hearing the soft sound of bunnies rustling in the bush, you repeatedly get the sound of Homer Simpson belching in your ear because that's what someone involved in the game arbitrarily decided would be for the best. This is type of solution that you are suggesting with your assume-default-value approach. Of course the natural response is to choose the least annoying sound\image\default value, but who decides that? Should we start adding focus groups and behavior therapists to our development cycles?
It's almost like your encapsulating too much. You're designing the library in this thought experiment, not the game itself. In this case the library should ask the person implementing it what to do. Maybe it's good enough for the bunny to sound like a chainsaw. Or maybe the bunny sound is a key component of some elaborate puzzle and the game isn't worth playing without it. That is up to the developer of the game to decide, not the developer of the library. Should a default value be made available? Absolutely, it's a solid idea. Should it be forced on the end user in every failure scenario? Not at all.
I haven't had the time to write a proper response earlier, but I have a bit of time available now.
I'm a fan of the "Crash Hard" approach for most errors. There is absolutely no reason to continue on and spit out errors for each subsystem that fails; It's better to crush them at the source. If a piece of your game/engine isn't working then you should abort (maybe display a simple non-trivial message) and debug the issue as soon as it starts, not ignore it simply because it isn't game-breaking. If you fail to stop bugs and ignore them, then good luck trying to debug code that depends on buggy code.
If you run out of memory, your disk fails, or other basic functionality fails to do what it's supposed to then pretty much the only safe thing to do is crash, not throw an exception.
If you run out of pool memory, you can try increase pool size. Run out of memory ≠ Physical memory is exhausted. For JVM it is a pretty standard situation for example.
your disk fails
That might means that it is been replaced or data is restored from backup right now, so program should postpone all disc operations (actual requirement of one program I worked with)
or other basic functionality fails
Losing network connection is pretty serios problem with financial programs. And those which does not work at all without network are all died out. I wonder, why? Common behavior is to let user work in special offline mode and commit transactions later. That includes saving current transaction (where connection died) and later double check if it was or was not applied.
pretty much the only safe thing to do is crash
Store all related information (often not avaliable to concrete low-level functions) in file which could be sent to developers so they could pinpoint problem and hopefully fix it.
not throw an exception.
That is funny. Because throwing unhandled exception is one (only?) of the RAII safe methods to crash program. Other ways to do this usually does not invokes destructors and defeats purpose of RAII.
If you run out of pool memory, you can try increase pool size. Run out of memory ≠ Physical memory is exhausted. For JVM it is a pretty standard situation for example.
This should be handled by the underlying function, the user of the function shouldn't be worrying about this.
That might means that it is been replaced or data is restored from backup right now, so program should postpone all disc operations (actual requirement of one program I worked with)
But it wouldn't be an error, you'd just be returning the state of the disk.
Losing network connection is pretty serios problem with financial programs. And those which does not work at all without network are all died out. I wonder, why? Common behavior is to let user work in special offline mode and commit transactions later. That includes saving current transaction (where connection died) and later double check if it was or was not applied.
Fair enough.
Store all related information (often not avaliable to concrete low-level functions) in file which could be sent to developers so they could pinpoint problem and hopefully fix it.
Well, I would send a crash report if it failed. But I would still crash the program, not let it run still.
What if I want to know that something bad happened?
"Oh by the way, we accidentally destroyed part of your house while we were building it, but we fixed it properly and completed ahead of schedule anyway. Wait, why do you need to know?"
helios wrote:
You hire a company to build a house for you somewhere. One day the roof caves in and, rather than calling you to inform you, they make an exact duplicate of the roof out of matches and glue because that's the best they had to work with. The next winter when you move in, it all falls down on you and kills you.
The best they had to work with? I paid them 16GB of ram and they spent it all on luxury pushbuttons for the doorbell?
chrisname wrote:
The fact that disorder exists in the universe.
I am too idealistic.
Computergeek01 wrote:
Of course the natural response is to choose the least annoying sound\image\default value, but who decides that?
The calling code decides that. "Give me what I ask for or else give me this alternative." Better than "Give me libiberty or give me std::terminate."
Avilius wrote:
I'm a fan of the "Crash Hard" approach for most errors.
By "most errors" you mean "the errors that I cannot eliminate as possibilities". I can't ignore the fact that the hardware allows for errors to happen. I can't ignroe the fact that the OS allows for errors to happen. I can't ignore the fact that the standard library allows for errors to happen. But I can certainly abstract that away myself.
"Oh by the way, we accidentally destroyed part of your house while we were building it, but we fixed it properly and completed ahead of schedule anyway. Wait, why do you need to know?"
"There's another house underneath the foundations (don't ask) and I need to tear it down if it gets disturbed. Now I can't know what state it's in because there's this new house in the way."
(I.e. rollback)
The best they had to work with? I paid them 16GB of ram and they spent it all on luxury pushbuttons for the doorbell?
You don't pay them in system resources, you pay them in contextual information.
So far I have been thoroughly convinced that my dislike for exceptions is unfounded, but have been playing stubborn for the sake of seeing what else I can get. I will do some thinking and make up my mind about how much and when I will use exception handling. I appreciate all the excellent discussion here!
You are coming up with many solutions to deal with errors, and therein lies an argument for exceptions.
Look at C. Some libraries use errno, some return 0 on success, some return -1 on failure, some have their own global error. We are lucky when we get a null pointer, think about stoi(), which returns 0 on failure. Is that 0 really a fail, or maybe my string was "0" ( or "00" or "0x00" ). Or maybe my string is "0" and something horrible happened inside stoi() that I wont know until some other part of my program tries to do something completely different.
This happens when a building descriptor references a model that can't be found. The game is still perfectly playable. Funny enough, one can find a brown box in the game's into movie.
I brought up C because I'm sure you are at least familiar with it and it is an example of a language that does not have exceptions. When we don't have exceptions we have thousands of people recovering from thousands of types of errors in thousands of different ways.
I decided to check what kinds of exception we use and found out this: Unhandled_request.
It is used by classes which provides abstract implementation of Chain of Command pattern. As it can be inferred from name it is thrown when concrete chain cannot handle request sent (because there were no handlers which can handle it).
All classes are neatly placed in "command_chain.h" header.
However, code using it is vastly different. There are at least three cases:
1) Concrete handle should handle all requests. If request is unhandled, it is a serious bug which requires fixing ASAP. Exception is left uncaught (actually it does get caught, but is rethrown) crash report is generated, developers got shouted at.
2) Filters and actions for handlers are loaded from customer-provided files. If it should crash or not depends on error handling strategy loaded from confguration file but default behavior is to continue working and write error message in console.
3) This class is used by Event handling. Unhandled requests are norm and expected, because few receivers of events can handle every single type of event. Exceptions are silently supressed.
If you suggest exception-less implementation of said classes which would work in all 3 situations, I am interested.