8 ms·
This is absurd. Exceptions are just generalised returns. You can use them well and benefit from them, or use them poorly and make the stuff of nightmares. It’s
by evincarofautumn 14y ago
This is absurd. Exceptions are just generalised returns. You can use them well and benefit from them, or use them poorly and make the stuff of nightmares.
It’s the same with any feature of any language. You can choose not to use exceptions in your code, if that makes it easier for you to understand and manage, but it’s certainly not the fault of the feature in general.
Basically, it’s up to you to structure your code well. Yes, it gets bad if a potentially throwing function can be called from multiple points. The error needs to be handled at every one of those points, and probably in much the same way.
But if you see repetition, why aren’t you factoring it? And why are you calling that function from multiple unrelated locations in the first place?
I get it. I’ve used C++ for half a lifetime—it’s a hopelessly broken language. There are tons of things about it that just make me furious. But I can get stuff done in it because I’ve taken the time to understand it in depth. Don’t complain about the language or a particular feature thereof just because it’s not suited to your problem or your way of thinking.
There are other languages out there (Erlang) that are far better suited to stable zero-downtime systems than C++. There are languages (Haskell, Lisp) that help you better manage complexity than C++. Every language implementation worth its salt has C bindings, so you can write performance-critical code at a low level, while still managing the whole system in a high-level language. There are options. Explore them!
- mccoyst 14y agoOne of his first examples is additionally absurd and is either the flimsiest of strawmen or he simply doesn't understand when exceptions should be used: try { ... int rc = fx (); if (rc != 0) throw std::exception ("Error!"); ... catch (std::exception &e) { handle_exception (); } Why is he throwing exceptions and catching them within the same function? His "C equivalent" is what he should've been doing in C++.
- onli 14y agoHe states how he understood exceptions: "C++ exceptions just didn't fill the bill. They are great for guaranteeing that program doesn't fail — just wrap the main function in try/catch block and you can handle all the errors in a single place." This is something i learned in my very first computer science lecture not to do. To be fair: It's what exceptions can be good for - a last barrier before a crash and a way to handle errors later. But you're absolutely right that he simply could react directly to the error. Or throw specific exceptions and react to them. His described issue has nothing to do with exceptions themselves, just with the way he thinks he has to use them.
- cobrausn 14y agoThough we probably learned this is not the way to use them, I have seen numerous real-world examples where this was the case. Not only that, I don't think C++ exceptions are necessarily very good at being a 'last barrier' before a crash - they simply can't catch all the errors, because they are good at indicating conditions that might be recoverable, not catching and recovering from logic errors that would cause a crash. There are plenty of ways to hang yourself in C++ without ever throwing an exception.
- figglesonrails 14y ago"Though we probably learned this is not the way to use them, I have seen numerous real-world examples where this was the case." Ah, Java code at my workplace. catch(Exception e) {}
- LoneWolf 14y agoI believe you mean catch(Throwable t) {}
- figglesonrails 14y agoExceptions indicate //exceptional conditions//, i.e. conditions other than the intended state transitions of the program. They aren't a guard against crashing, they are a mechanism for formalizing error states in the program. Memory corruption? Appropriate response: fail. Logic error? Appropriate response: fail. Exception? Appropriate response? Decide if program can continue, respond. I'd say the bigger issue is less of "exceptions" so much as "states". Adding any kind of error handling increases the number of state conditions that your program may be in, and that complexity is obscenely hard to manage. I don't think C or C++ or Java or Erlang or any other language will free you from the burden of proper software engineering.
- batista 14y ago>He states how he understood exceptions: "C++ exceptions just didn't fill the bill. They are great for guaranteeing that program doesn't fail — just wrap the main function in try/catch block and you can handle all the errors in a single place." This is something i learned in my very first computer science lecture not to do. Yes, and he learned not to do it also, thank you. He makes a tongue in cheek comment about a common Exceptions abuse. Actually, in the very next sentence after than one, he explains why that is bad. Do people on Hacker News believe that the guy doesn't know C++? He is the frigging author of one of the most useful, used, high quality C++ libs.
- rocha 14y agoI agree, it was disconcerting to me as well. I read again and now I think that he is putting the throw there to exemplify that fx() can throw an exception.
- politician 14y agoAn ellipsis means something unimportant has been elided. For example, try { foo(); catch (std::exception &e) { handle_exception (); } int foo () { int rc = fx (); if (rc != 0) throw std::exception ("Error!"); }
- cageface 14y agoThe combination of manual memory management and exceptions is particularly nasty. When you can't count on a GC to clean up for you every function has to be carefully coded to prevent potential leaks. Browse through some of Herb Sutter's GOTW questions to get an idea of exactly how tricky this can be.
- evincarofautumn 14y agoIdiomatic C++ does not rely on manual memory management—it relies on deterministic memory management. Use unique_ptr, or shared_ptr when it makes sense (hint: it rarely does).
- cageface 14y agoIn theory you have every single leakable resource wrapped up in a RAII container or a smart pointer. In reality you're almost always dealing with raw pointers coming from a lower level C API or old C++ code or some kind of object handle you haven't wrapped yet. Of course the recommendation from Stroustrup & Sutter is to use the new smart pointers for everything but I think it will be a few years at least before most people can follow that advice.
- ajross 14y agoSure. And using deep-thrown (i.e. thrown across module/library boundaries -- often exceptions are local tricks used within tightly coupled code) exceptions in an application where you're dealing with such pointers would be bad design.
- evincarofautumn 14y agoTypically what I do in that situation is lightly wrap the API. For instance: Image* image = allocate_image(…); … deallocate_image(image); ↓ struct ImageDeleter { void operator()(void* image) const { deallocate_image(image); } }; unique_ptr<Image, ImageDeleter> image(allocate_image(…)); That certainly can’t work everywhere, but I have yet to be failed by it.
- wvenable 14y agoI agree the whole rant about exceptions just seems wrong -- if you've got a state that you can handle right there in the function itself then it's not an exception. You just deal with it. The C code and the C++ code would be equivalent. This feels more like a "Oh I have exceptions, I better use them" situation. And his solution is to pick a language without exceptions so he doesn't have to use them. It's all very odd.
- danssig 14y agoOr in Common Lisp you would be able to "signal" a condition and then handle the situation. Signal will just do nothing unless some higher level code has an explicit handler for it [1]. [1] I remember reading a MIME encoding library years ago. There was a point where the code would detect an error but if the error was at the end it would just drop that part and write in the last couple of bytes. In this situation, most clients won't care that this is happening but what about those that do? Since this code signalled error by returning NULL there was no way to signal "saw an error, but fixed it". In Common Lisp, signal could have been used for clients that would want to report on this.
- calinet6 14y agoBingo. When I read this: "C++ exceptions just didn't fill the bill. They are great for guaranteeing that program doesn't fail — just wrap the main function in try/catch block and you can handle all the errors in a single place." That's not C++'s fault, or Exceptions' fault in general: that's your fault for using an extremely useful and versatile language construct completely incorrectly. If a high-level language is bad because it offers you more functionality and features than you know how to use, then sure, maybe you shouldn't be using a high-level language. But this argument is almost like decrying being rich because you don't know how to use your money wisely; trust me, it's still better to have money, and you shouldn't throw away your high-level life just because it's "simpler to be poor." If your goal is to do high-level complex things, it's better to use a high-level language. If your goal is to do low-level pedal-to-the-metal things, then it's better to use a low-level language. But the simple fact that a high-level language is high-level is not a valid criticism. Not of the language, anyway.
- felipemnoa 14y agoI agree with your entire post. This part made me laugh though: >you shouldn't throw away your high-level life just because it's "simpler to be poor." It made me think of people that have won the lottery and after they've finally blown through all their winnings many concur that: it's "simpler to be poor."
- ryanmolden 14y ago>just wrap the main function in try/catch block and you can handle all the errors in a single place. Yeah, this kind of statement always scares me. In my experience people proposing it tend to 'handle it' by simply ignoring it happened. As if by simply catching and not crashing you have averted disaster. Nevermind that very little, if any, code is truly exception safe. That exception just tore through N frames of code interrupting each one at who knows what step and who knows what kind of state the object is now in. If you were lucky they were at least using RAII objects to ensure they haven't leaked things, but that is only half the battle. Unless they are using some kind of transactional approach to mutating their own instance state you likely have an object in a 'half-transitioned' state, which will likley violate various invariants the original author assumed (incorrectly) would always hold. Continuing in C++ (or really any language) after swallowing an exception 'around main' is a supremely bad idea, unless you like heisenbugs or getting pwned.
- 0xABADC0DA 14y agoChecked exceptions are great, but unchecked exceptions are a huge disaster only mitigated by failsafe features such as RAII or garbage collection with finalizers. Sometimes like in highly dynamic languages the failsafes are enough that it's ok to just use unchecked exceptions, but in a language like C++ you really can't afford to make mistakes in the first place. The process of adding an exception in Java: 1. throw TheException 2. compile 3. add "catch(TheException e)" to fix errors or "throws TheException" to signature 4. repeat 2 until no more errors Compare to C++: 1. throw TheException 2. examine all functions to determine if they might need to handle TheException or consult intuition about program structure 3. repeat 2 until 'done' So the real problem with exceptions in C++ is the compiler can't tell you where to look next and what you forgot about. Step 2, magically find all the places that should catch the exception, is both time consuming and error prone. I find it strange that C++ has all kinds of strictness with types (have to cast void*, const virus, etc) but with exceptions they just throw caution to the wind.
- MatthewPhillips 14y agoJava is the only language that I can think of that gets exceptions right, in that it forces you to acknowledge them every step in the chain and choose whether you're going to catch them or throw them upwards.
- evincarofautumn 14y agoHaskell does this also, and its type system is much more expressive than Java’s, enough so that exceptions are not (exclusively) a built-in language feature.
- SeanLuke 14y agoStrange. It seems to me that the consensus in the Java community is that checked examples were among the worst misfeatures of the language. Googling for ---java checked exceptions--- is illuminating.
- MatthewPhillips 14y ago
- malkia 14y agoIt's not absurd. Just the fact that C++ exceptions were used, would make this code not portable - not being able to run on some very popular devices.
- Peaker 14y agoReturns are statically typed. Exceptions are not. In a static language like C++, this is a big disadvantage.
- dkarl 14y agoAgreed. His examples are nonsensical, in almost every case comparing handling an error in C with signalling an error in C++. To see what I'm talking about, here's his first comparison: // C int rc = fx (); if (rc != 0) handle_error (); // C++ int rc = fx (); if (rc != 0) throw std::exception (); This is absurd. This is not the same thing in two different languages. These two pieces of code do two different things. One handles the error, and the other signals that there's an error that should be handled elsewhere. For an apples-to-apples comparison, let's consider both cases in each language. If this is the appropriate place to handle the error, then you handle it. It looks like this: // C int rc = fx (); if (rc != 0) handle_error (); // C++ int rc = fx (); if (rc != 0) handle_error (); I.e., exactly the same. If you DON'T want to handle the error here, then you return an error code or throw an exception: // C int rc = fx (); if (rc != 0) // There's no way to know where or whether this will be handled return some_error_code; // C++ int rc = fx (); if (rc != 0) // There's no way to know where or whether this will be handled throw some_exception(); You're in the same boat in C and in C++. The error must be handled elsewhere, and nothing you do here can ensure that it will be. Exceptions have some advantages, though: 1. You do not have to anticipate inconvenient collisions between error return values and valid return values. 2. If the calling function is not the appropriate place to handle the error, then no boilerplate is required in the calling function to pass the error up the stack. 3. Exceptions let you use destructors instead of explicit cleanup blocks. 4. Failure to write error-handling code will not result in an operation proceeding in an invalid state, deceptively reporting success, or silently corrupting state. An error return value can simply be ignored. An unhandled exception will cause a cascading failure that cannot be mistaken for successful completion of an operation. 5. You can always use return values to report errors if they happen to be preferable in some cases. P.S. The more I understand his complaints, the more I think he is just trying to get too fancy. If construction and destruction are problematic for some objects, then classes with trivial constructors and destructors give him exactly the struct-like behavior he wants, so what is wrong with that? How in the world do you get into worries about exceptions thrown from constructors and half-initialized objects (exception in the sense that any allocated-but-not-initialized struct is in a "half-initialized" state)? This is like saying knives are too dangerous to have the in the kitchen because you are in the habit of thrusting your hand blindly into the knife drawer.