3 ms·
> If you're wrapping a try block around any code that might throw an exception, then you're doing it wrong. CS student here, can you elaborate on this? What co
by chuckdries 9y ago
> If you're wrapping a try block around any code that might throw an exception, then you're doing it wrong.
CS student here, can you elaborate on this? What code would you wrap in a try block if not code that could throw an exception? Are you suggesting try blocks should only be used to insulate function calls we know to be unsafe?
- ruleabidinguser 9y agoThe point is to handle exceptions in appropriate locations, and pass exceptions up if you cant handle them. I think theyre saying that throwing a try block around an exception just to avoid dealing with it is a problem
- Suncho 9y agoSorry. I said "any code," when I meant "every piece of code." Edited. C++ programs normally throw an exception when a C program would call exit() or abort(). So I would start by having one try block that surrounds your main() function with a catch-all block that prints some sort of error message before your program terminates. This is often all you need. If a situation arises in which you can handle an exception without terminating your program, you can throw a try block in there too. Exceptions automatically provide for you what you would write by hand in languages like C: // The C way int error_flag = EVERYTHING_IS_OK; int checked_div (int numerator, int denominator, int *quotient) { if (denominator == 0) return 1; *quotient = numerator / denominator; return 0; } int foo (int a, int b, int c) { int q; int err = checked_div (a, b, &q); if (err) { error_flag = FOO_DIV0; } return q+c; } * // The exception way (notice that there's no try block) int checked_div (int numerator, int denominator) { if (denominator == 0) throw runtime_error ("Divide by zero!"); return quotient; } int foo (int a, int b, int c) { return checked_div (a, b) + c; } Clearly, if you don't use built-in exceptions, you have to manually simulate the exception mechanism. The most popular strategies for simulating exceptions involve some combination of systematically returning and checking error codes or systematically setting and checking global error flags. I've demonstrated both above. The linked article is an example of the first strategy. Returning error codes prevents functions from directly returning their results. The "eithers" strategy is an attempt to make this a little smoother, but it doesn't solve the problem that it makes your code weird. Global error flags have their own host of problems. If you fail to check for errors, havoc will ensue. Furthermore, multithreaded environments turn global error flags into a tangled web of deceit. Nevertheless, these ad hoc strategies often suffice because the unusual nature of exceptional errors can prevent error handling code from clogging up your primary code path too badly. But even for simple situations, the compiler can do better than you can. Why add unnecessary complexity to your code? You should write try/catch blocks only when you know how to cope with exceptional errors. Needless to say, they should be pretty rare. But lets say that when a particular invocation of foo() fails, we want to use the maximum integer value as our result. Here's how it looks: // The C way int bar (int a, int b, int c) { int result = foo (a, b, c); if (error_flag != EVERYTHING_IS_OK) { assert (error_flag == FOO_DIV0); result = numeric_limits<int>::max(); error_flag = EVERYTHING_IS_OK; } return result; } * // The exception way int bar (int a, int b, int c) { try { return foo (a, b, c); } catch (runtime_error) return numeric_limits<int>::max(); } } Hmm... The exception version doesn't look that much simpler. After all, the assert() call will be compiled out of release builds. But C++ compilers can, and most modern ones do, generate code for the exception version as if it were written more like this: int bar (int a, int b, int c) { return foo (a, b, c); } // This is only called when a runtime_error exception is thrown. int bar_catch (int a, int b, int c) { // return to the same place bar() returns to return numeric_limits<int>::max(); } That's right. Even in the presence of a try/catch block, the code for foo is generated without the try/catch block. The error handling code gets stored somewhere else so it won't take up valuable cache space. Of course, what the compiler generates doesn't look exactly like my example, but the effect is the same. And this strategy works even for more complicated functions. The compiler creates a non-optimized version of the function. Then, when an exception gets caught, the non-optimized function can simulate the state to resume from. This exception handling strategy is often referred to as the zero-cost exception mechanism. The latest versions of Clang, GCC, and 64-bit VC++ all implement some variation of it. For compilers that don't, there's a cost to adding try/catch blocks to your code. But it's still cheaper than the cost of your own error checking. Even if it tried, I don't know if a compiler could possibly implement exceptions in a way that's slower than a manual error handling approach. There's a myth out there that exceptions add overhead or make the control flow of a program more difficult to reason about. Of course, this is the opposite of what they do. Even if you're working with C-like code that has no user-defined classes, switching to exceptions is a win. Exceptions enable some of the fundamental abstractions in C++. How else are you going to handle a memory allocation error in an implicitly-called copy constructor? If you'd like to get a sense of just how perversely you need to contort the C++ language in order to deal with the absence of exceptions, look no further than Google's C++ style guide. Google doesn't use exceptions in their C++ code. This policy has ripple effects throughout their guidelines. Among other things, they forbid constructors from allocating resources. https://google.github.io/styleguide/cppguide.html https://google.github.io/styleguide/cppguide.html Google's excuse for not using exceptions is that their existing exception-free code base is so large that it would take too much effort to convert it. But if they can interface C++ code with Java/Python/Go code, they should have no problem interfacing exception-free C++ with exception-full C++. I can't help but wonder if some of their engineers are confused about how exceptions work. Anyway, if you don't work for Google, use C++ exceptions. Wow. That turned into a rant (some of which was copy-pasted from something I wrote a while ago).
- aardvark179 9y agoHaven't checked CPython's extension API but Java's JNI does not allow exceptions to propagate through native code. When you call a Java method from JNI it will return and you have to check whether there is a pending exception, and do whatever is sensible in your native library. If a native library called from Java throws an exception that is not caught in that library then I think all bets are off - it probably depends on your language/platform ABI. The best case scenario would be that your Java stack gets nicely unwound in some way, but given it wouldn't be a Java exception object being thrown up the stack I'm really not sure what would happen. Unwinding the stack through native library or language boundaries is fraught with potential problems and language interop APIs have to be designed carefully round that sort of thing.