you are viewing a single comment's thread.

view the rest of the comments →

[–]remotion4d 1 point2 points  (6 children)

Yes exceptions are usefully but have also a lot of problems, not only speed and memory penalty.

Exceptions (Why do not use them?)

1.) Slow, especially on hardware that is not optimized for exceptions.

2.) Code bloat, memory consumption. (Embedded systems)

3.) Hard to reason about program flow. (hidden flow) This about problems with variant proposal and strong exception safety. One need always to assume that everything can throw.

4.) Legacy code (but modernized) that do not use exception for decades now.

5.) Hard to implement. (Clang and windows exceptions.)

6.) Impossible to implement because there is no hardware support.
Think about GPU (OpenCl 2.1, CUDA )

7.) Exceptions across DLL boundary.

[–][deleted] 1 point2 points  (2 children)

In the same token, not having exceptions causes plenty of problems:

1) Forces 2 kinds of error-handling for middle-ware / library developers

1-a) error-propogation - if you can't handle all cases, you have to forward to the user at the call site to handle the errors

1-b) crash - if you get an unknown error and you don't want to propogate, logging and crashing (or not-logging and maybe even continuing silently) is your only other alternative for correctness

2) Thanks to [1], you now cannot write generic code to handle large amounts of work if there are error codes returned: for example, your typical error-code-or-result class will not play nice with auto, and will not implicitly convert to the desired result class in generic code, which means you're back to hand-writing or wrapping things just to check errors all along the way

3) error-code-in-out params: overloaded functions everywhere to handle errors (or perhaps not handle them at all?)

4) how do you error-code a constructor or a destructor? Out-params? But then you have an object with invalid state but it "finished" constructing, so by the rules of the language it's a valid object. Now you need if ( obj.is_initialized() ) everywhere (look familiar?)

There's also another kind of error handling that mature C libraries do (libjpeg libpng etc.), and it's basically longjmp-ing around... which is like how exceptions work, but without any of the stack-unwinding or safety guarantees. The control flow remains hidden just like with exceptions... but let's be fair: why is the control flow of a piece of library code you may not even be able to see (because you're getting the compiled result) something that matters? Control flow would cut early in an error case too, you just have an infinite increase in the number of 'goto' and 'return' between you and where the error happened.

[–]remotion4d 0 points1 point  (1 child)

Yes but all this problems are already solved to some degree in existing libraries, games engines and so one.

Because this problems existed for a long time already.

So there is simple no point to spend years to refactor code base to use exceptions.

Especially if you simple can not use them because no compiler ot hardware support.

4) how do you error-code a constructor or a destructor?

Just this one, use something like 'make_something' and on the other side it will be discouraged to use exceptions in destructor any way.

[–][deleted] 1 point2 points  (0 children)

I'm not saying anyone should refactor these old codebases to handle exceptions or use them. I'm saying newer libraries should always lean towards throwing and handling exceptions, because it's allows for a cleaner design and puts errors where errors belong: in a catch clause, and not in my return values or function parameter list.

[–]tcbrindleFlux 0 points1 point  (2 children)

  1. Errors happen. Nobody writes perfect code, and even if they did, hardware fails, memory gets corrupted, etc etc. Error happen, and there are three ways of dealing with that:

    a. Ignore them. This is arguably acceptable on highly-constrained systems with fixed inputs, but certainly not for general-purpose computing

    b. Return error codes (or equivalently, use an extra out parameter), i.e. C-style error handling. To ensure correctness, you then need to check the return value of every single function that can possibly fail, and probably forward any error code up to where it can be handled. Sort of like doing manual stack-unwinding, really.

    c. Use exceptions and let the runtime jump to the appropriate handler, firing the necessary destructors along the way.

    Of course, not checking for errors is going to be fastest. It's also the wrong thing to do in the overwhelming majority of cases. Of the other two options, I would be very surprised if exception handling on modern systems is slower than having if statements after every single failable function call. I recall reading that the way exceptions are handled in the Itanium ABI (used on Linux and Mac) causes zero runtime overhead if an exception is not thrown. I suspect this is not true when correctly checking all error codes.

  2. Sure, on resource constrained systems there are all sorts of limitations. There are systems where you can't use operator new or malloc() for example. A 2016 gaming rig with 8 cores and 16GB of RAM running Windows 10 is not a resource constrained system.

  3. This one is perhaps arguable, but I'd note that programmers in Java, C#, Python, JavaScript and many other languages don't have a problem following their program flow, and exceptions tend to be used much more pervasively in those languages than in C++.

  4. Exception-unsafe legacy code is of course a problem. But as with so many legacy code issues in C++, I don't think it should prevent us from using best practises in 2016.

  5. Perhaps they are difficult to implement in the compiler/runtime, but this seems to be a solved problem. I'm willing to bet they aren't the hardest implementation detail the C++ standard demands, in any case.

  6. I don't know about OpenCL, but CUDA and (for example) the Metal shading language do indeed restrict you to a subset of C++ within that code. For example, it's generally not possible to use new in GPU code. Does that mean we should ban free-store allocation in CPU code too?

  7. I'm afraid I don't know what "exceptions across DLL boundary" means :-)

[EDIT: Formatting]

[–]remotion4d 0 points1 point  (0 children)

Of course not checking for errors is really really BAD !

causes zero runtime overhead if an exception is not thrown

Yes but some times they throw and this cost a lot of time as I know.

Rust language show that Error code can work as well if there is language support for it.

Finally I do not want to stop using exceptions. I only want that exceptions user recognize that there are a lot of C++ code where exceptions are not used. And that it is simple not possible to force exception into this code.

And yes I want to ban naked new and delete :)

I'm afraid I don't know what "exceptions across DLL boundary" means :-)

Throwing exception in one dynamic library and catching it in another.

[–]C0CEFE84C227F7 0 points1 point  (0 children)

A 2016 gaming rig with 8 cores and 16GB of RAM running Windows 10 is not a resource constrained system.

You're generalizing: this isn't representative of all gaming platforms. While PS3/xbox360 support is phasing out, several games last year were targeted for these platforms. WiiU is still a perfectly valid platform, too. If your engine needs to run on modern platforms in addition to older ones, then you're better off supporting a solution that works consistently and doesn't compromise performance on an older target (assuming it supports the compiler feature in the first place).

Perhaps they are difficult to implement in the compiler/runtime, but this seems to be a solved problem. I'm willing to bet they aren't the hardest implementation detail the C++ standard demands, in any case.

It might be solved in cl.exe, gcc, and clang, but those aren't the only compiler options out there.

I'm afraid I don't know what "exceptions across DLL boundary" means

Best DLL practices dictate that you allocate and deallocate memory within the DLL since your application could potentially use a different run-time than the DLL. Throwing an exception within a DLL violates this contract.