8 ms·
Read this paper before and don't buy it. // c++ if (condition1) throw my_exception1 (); you throw an exception because you can't handle it locally
by zasdffaa 4y ago
Read this paper before and don't buy it.
// c++
if (condition1)
throw my_exception1 ();
you throw an exception because you can't handle it locally. But then he says
// C
if (condition1)
handle_exception1 ();
so if it can be handled locally in C it can also be in C++, so why throw anything?
Also, and I'm not a C++ guy (a little exposure many years ago) so someone correct me, but if you look at part 2 of this article (link at bottom to https://250bpm.com/blog:8/ https://250bpm.com/blog:8/) he then complains about having to have helper data structures
// c++
class person
{
int age;
int weight;
};
std::list <person*> people;
vs
// C
struct person
{
struct person *prev;
struct person *next;
int age;
int weight;
};
struct
{
struct person *first;
struct person *last;
} people;
"First thing to note is that the C++ solution allocates twice the number of memory chunks compared to the C solution. For each element of the list there's a little helper object created." [he provides a diagram too]
But surely you can roll those helper objects into the main structure with templates - that is possible, right? OK, you don't use the library util 'std::list <person*> people' but still, it's possible and straightforward, right? Edit: and this was possible in 2012's version of C++ too I'm sure.
- idorosen 4y agoYes, there was boost::intrusive_list for the latter since before 2012, for example.
- leni536 4y agoI don't get it, just use `list<person>`. The prev and next pointers live next to the person objects right in the same node, no need for intrusive pointer. edit: The author assesses that this is somehow bad for person objects that are expensive to copy. But std::list has splicing operations, and since C++11 it has emplace as well, so this doesn't apply.
- esprehn 4y agoThere's no way to use that optimization if you need to be in two or more lists at the same time.
- lumost 4y agoWouldn't std shared pointer handle that case?
- john_fushi 4y agoWhich is also an issue in the author's example, no? So the point would be moot.
- abetusk 4y agoThe point is not that you're handling an exception with some function, it's that knowing which function is being called to handle the exception becomes difficult to reason about. This is the relevant line in the article: > The problem with that is that you have no idea of who and where is going to handle the exception. If you don't have a try/catch immediately around the throw, say at a higher level of where the function was called or, in the "worst" case, in the main body block, then the handling becomes hard to reason about. I believe the argument is that reasoning about who handles which exception when is difficult and putting in logic to handle each individual exception at a higher level increases program complexity. Further, hunting back through the code to which piece of parent call handles the exception becomes increasingly difficult, especially if a function is called at different points with their own try/catch blocks. The problem compounds because if you use classes, then there's no way to get around using exceptions as that's the only way to communicate that a constructor has failed. This means if you wanted to abandon C++ exceptions in favor of C-style error handling but still wanted to use classes, you would now need to mix and match C-style error handling with C++ style exception handling. I don't quite see the argument against the class/struct <list> issue and I agree that this, from what I can tell, could be worked around and has better alternatives in C++ than they're making arguments against.
- jamier1978 4y agoTo me the two bits of code in the error handling case or not comparable. In the c++ case you would still handle the error in the if statement if it can be handled otherwise you would throw it. C++ has the advantages of giving you both options.
- arinlen 4y ago> The problem with that is that you have no idea of who and where is going to handle the exception. I don't buy the argument. By design exception handlers are handlers of exceptional events, which due to their exceptional nature either lead to a controlled recovery or controlled exit. Once you're faced with your personal need to care enough about specific types of exceptions to the point they are no longer exceptional but a common occurrence, you're already mishandling exceptions at a fundamental level. Consequently the solution is to not handle these expected cases as exceptions, or at most add try-catch next to the potential source of these commonly-occurring exceptions and don't let them leak beyond their source. Exceptions are a kin to fire alarms and exception-handling is a kin to an organized evacuation plan. If you're misusing the fire alarm to the point you're complaining that you pull a particular fire alarm so often during your normal workflow that you start complaining you can't track badges during emergency evacuations, you should take a step back and take a long hard look at what you're doing, and how you're misusing that safety feature.
- thesz 4y agoC++'s throw lets you postpone handling of erroneous condition. You throw, then sort. You can postpone sorting of causes indefinitely, basically, up to the level where all of context needed is lost. In C, you cannot throw. You have to handle. There will be more "if" statements, that's price to pay. I work on code in C and C++ in more or less close problem domains and C++ provokes style and/or design that is much more hard to work with. One of culprits is deep inheritance - once built, the inheritance structure cannot be changed. And I saw inheritance trees as deep as 10 recently. Instead of refactoring, I see copy-paste.
- cletus 4y agoI honestly think exceptions will go down in history as a design mistake. Newer languages (eg Rust, Go) doesn't have them. Exceptions make it incredibly difficult to be able to follow program flow. Most of the time you just log an exception and then either move on or exit. There's almost no actual handling of an except (other than maybe retrying). It gets even worse when people start using exceptions for control flow rather than errors. One note about your example: > std::list <person*> people; You wouldn't do this in C++. Why? Because ownership isn't clear meaning if people is cleaned up, it won't clean up the elements automatically. You could do: std::list<person> people; but you have to be aware of copy semantics. You might do: std::list<std::unique_ptr<person>> people; but that gets awkward too.
- vips7L 4y agoThe differences between Go and Rust error handling is that Go doesn’t force you to handle its errors. Nothing bad happens if you just forget or don’t handle the error. In Rust if you just unwrap it’s going to panic and in exception based languages it’s going to propagate until someone handles the error. I personally don’t think Go error handling is going to go down in history as good.
- OJFord 4y agoI don't like Go error handling either (and I do like Rust) - but if the argument is against exceptions because of confusing flow, then there's a case for it in some languages I think - e.g. it's what you'd choose for Python if nobody had come up with exceptions (or they fell out of favour in time) for example.
- cletus 4y agoI agree Rust's error handling is better and you can criticize some decisions in Go's error handling. I mean it also has these weird hybrid exception-like things (ie panics). But the main point is that Go (and Rust) moved away from exception-as-control-flow that C++, C# and Java had adopted.
- 4y ago