Why exceptions




















Go does not have exceptions, so people use return codes instead. A stack trace would have been super nice. Generating accurate stack traces is harder than it looks. Optimising compilers love to inline functions into each other. But if your functions have all been splatted together, how do you retrace the steps the program took to reach a throw site?

Cutting edge runtimes like the JVM or. NET CLR want performance and convenience, so their just-in-time compilers are specifically designed to generate lots of metadata along with the machine code. The metadata allows the stack trace generator to unpick inlining and other optimisations that could corrupt stack traces. Generating and then mapping the real machine stack back through all the metadata tables is complex and time consuming, so state of the art JIT compilers like HotSpot will do things like recompile methods on the fly to disable stack trace generation, if profiling shows that your code is throwing and catching exceptions way more often than expected for instance, as part of normal operation.

This bit of Java code is exception safe:. If the loadFrom method throws an exception, the global list is untouched. If it keeps happening, we can eventually exhaust our heap and crash. The syntax is different now. Objects that are thrown must have a publicly accessible copy-constructor.

The compiler is allowed to generate code that copies the thrown object any number of times, including zero. That way, the programmer cannot forget to release the resource. Basically, someone resuming from an exception handler can never be sure that the code after the point of throw was written to deal with the execution just continuing as if nothing had happened. To get such code right, the writer of the throw and the writer of the catch need intimate knowledge of each others code and context.

This creates a complicated mutual dependency that wherever it has been allowed has led to serious maintenance problems. If you want to check to see if you can fix a problem before throwing an exception, call a function that checks and then throws only if the problem cannot be dealt with locally. Exceptions and Error Handling Why use exceptions? Writing code with error-return codes and tests is not free either.

It costs nothing on some implementations. You incur cost only when you have an error. If a computation takes too long someone may die. In that context, even free store allocation is banned! How do I use exceptions? Do not use throw to indicate a coding error in usage of a function. Use assert or other mechanism to either send the process into a debugger or to crash the process and collect the crash dump for the developer to debug.

Do not use throw if you discover unexpected violation of an invariant of your component, use assert or other mechanism to terminate the program. Throwing an exception will not cure memory corruption and may lead to further corruption of important user data. By eliminating one of the reasons for if statements.

Although the return code technique is sometimes the most appropriate error handling technique, there are some nasty side effects to adding unnecessary if statements: Degrade quality: It is well known that conditional statements are approximately ten times more likely to contain errors than any other kind of statement. Slow down time-to-market: Since conditional statements are branch points which are related to the number of test cases that are needed for white-box testing, unnecessary conditional statements increase the amount of time that needs to be devoted to testing.

Increase development cost: Bug finding, bug fixing, and testing are all increased by unnecessary control flow complexity. Because exceptions scale better than return-codes. That is badness: It clutters functions f2 through f9 with extra decision logic — the most common cause of bugs. It increases the bulk of the code. It clouds the simplicity of the programming logic in functions f2 through f9. How do exceptions simplify my function return type and parameter types?

Exceptions, when done right, separate the happy path from the error path. Go back! Here are some of the costs: Exception handling is not a free lunch. It requires discipline and rigor. Exception handling is not a panacea. If you work with a team that is sloppy and undisciplined, your team will likely have problems no matter whether they use exceptions or return codes.

Incompetent carpenters do bad work even if they use a good hammer. Exception handling is not one-size-fits-all. This is part of the discipline: you need to know when a condition should be reported via return-code and when it should be reported via an exception. Exception handling is a convenient whipping boy. If you work with people who blame their tools, beware of suggesting exceptions or anything else that is new, for that matter.

Learn how. You absolutely might be the problem! Organizing the exception classes around the physical thrower rather than the logical reason for the throw: For example, in a banking app, suppose any of five subsystems might throw an exception when the customer has insufficient funds.

The right approach is to throw an exception representing the reason for the throw, e. For example, the Foo subsystem might throw objects of class FooException , the Bar subsystem might throw objects of class BarException , etc. In general, exception classes should represent the problem, not the chunk of code that noticed the problem. When these three logically distinct kinds of errors are represented by the same exception class, the catchers need to say if to figure out what the problem really was.

If your code wants to handle only bad account numbers, you need to catch the master exception class, then use if to determine whether it is one you really want to handle, and if not, to rethrow it. Designing exception classes on a subsystem by subsystem basis: In the bad old days, the specific meaning of any given return-code was local to a given function or API. That causes no end of grief, e. In large systems, exception hierarchies must be designed with a system-wide mindset.

Exception classes cross subsystem boundaries — they are part of the intellectual glue that holds the architecture together. However you discover that somebody somewhere is sometimes passing nullptr anyway.

There are two possibilities: either they are passing nullptr because they got bad data from an external user for example, the user forgot to fill in a field and that ultimately resulted in a nullptr or they just plain made a mistake in their own code.

In the former case, you should throw an exception since it is a runtime situation i. I have too many try blocks; what can I do about it? My catch clause deals with the error and continues execution without throwing any additional exceptions. My caller never knows that the exception occurred. My catch clause does not throw any exceptions and it does not return any error-codes.

For instance, if you have a try block whose catch clause closes a file then rethrows the exception, consider replacing the whole thing with a File object whose destructor closes the file. This is commonly called RAII. Can I throw an exception from a constructor? The further it has to look up the stack, the more work it has to do.

Now for the final results, in which we explicitly ask the run-time to lazily fetch the full stack trace, by accessing the StackTrace property. The code looks like this:. Finally we see that fetching the entire stack trace via StackTrace dominates the performance of just handling the exception ie. But again, the deeper the stack trace, the higher the cost.

NET world, where huge stack traces are rare. Over in Java-land they have to deal with nonesense like this click to see the full-res version!! Performance is a Feature!



0コメント

  • 1000 / 1000