The "nothrow" is not part of the function signature, probably in order not to break retro-compatibility. Without this, "nothrow" cannot be enforced reliably at compile time. And I think they don't want to add a new sort of undefined behaviour if a function from a linked module doesn't respect the nothrow promise at runtime.
You are right, even nothrow doesn't fix the weirdness with signatures. However, nothrow allows a program to change behavior based on whether a function it will call will throw exceptions. That wasn't available under the old exception specification mechanism.
One of the ground rules for the old exception specifications was that it had to be possible to link code compiled at different times. Which meant that there might never be a moment when the compiler had access to all the source code. And, given that most code did not have exception specifications, the only way to enforce them at compile time would have been to require them on all functions.
I didn't follow the development of noexcept, so I can only guess at the motives. By and large, there were three complaints about exception specifications that I am familiar with: (1) by requiring stack unwinding when a no-throw specification was violated, they often caused worse performance (or, at least, didn't allow an optimization opportunity); (2) generally, the important question is whether a function might throw, not which exceptions it might throw; and (3) the rules around when exception specifications affect a function's signature -- e.g., does a pointer to a function have to match the specification exactly? -- were convoluted. I think it was reasonable for the Committee to focus on those issues.
A possible solution would have been optional static enforcement of exception specifications plus fallback on the existing dynamic enforcement.
Existing unmarked functions would be expeced to possibly throw everything, while functions with throw specifications would be statically checked.
You can call the later from the former, but to do the reverse you need an explicit try/catch. Some form of throw(auto) would enable automatic deduction of exception specifications for e.g. template code.
Dyanamic exception specifications will be removed fron the language in c++17, so the synax is open to be reused for such a feature in the future.
A lot of large C++ shops re-implement much of the standard library, often to add niche features that don't make sense as a standard. I believe replacing the allocator in a lot of the standard data structures to a non-throwing one will get you pretty close to non-throwing.
The standard libraries tend to only use them for fatal conditions such as out of memory. The standard library can compile with no-exceptions (see https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_except... ); if you mix no-exception and exception-using code, what happens is implementation-dependent but usually involves crashing.
When I first used Qt, I wondered how such a large framework could get along without exceptions. After writing some non-trivial Qt programs, I still noticed that there is really no need for exceptions, except when interfacing non-Qt libraries, where I simply caught and handled the exceptions in a small wrapper.
Finally, I got it: Qt uses the Null-Object pattern (not to be confused with Null values!) consistently, which is really in almost all cases the more elegant solution:
I think the general concensus now is this is a result of history and now the interia of the existing code base and practices makes it implausible to switch. According to some, if Google were starting over now with C++ compilers and language standard at where it is now, likely exceptions would be permitted.
A few months ago I came across a great resource with flags for making C++ behave a bit more like C with flags like -fno-exceptions and -fno-rtti. This is useful when working with embedded devices or if it just makes you more comfortable. Does anyone know any more?
Do you do RAII? It effectively forces exceptions, because nontrivial initialization necessarily means the possibility of failure, but exceptions are the only mechanism available for constructors to report failure (well, except for even worse things like an errno-like global variable).
- basic non-throwing initialization only constructor
- a "bool Initialize" method which does what the constructor would have done, but returns false on fail
- destructor as usual to clean up
But this is because I'm at Google where exceptions-are-no.
Well I don't know about that. If you have a datatype whose constructors can't throw, it should still assert at run time about its state that it's been initialized. Like, you have to actually do that, of course. So there's more surface area for mistakes inside the object, but from the outside of the object, which is what I think this sub-thread is talking about, they get caught at run time.
It shouldn't have to be assertions though - you should make it correct by, well, construction. When the fields are RAII the constructor can just initialize them. If your class has different possible states and flags it's usually correct to promote the different states to differnt types (subclasses).
I don't think we're disagreeing about anything. The scope of my comment is in reference to "So how do you protect yourself against inadvertently passing around uninitialized objects?" The answer is, with a run-time error.
But all failure avoidance systems rely on user caution. Error return values have to be checked. Exceptions have to be caught and handled (correctly). RAII has to be done everywhere a resource is allocated.
Now, true, some of these impose lesser burdens than others. Arguably, RAII imposes the least - but not zero. And if you forget with any resource, for that resource you are vulnerable.
That we can't make things perfect is no reason not to make things better.
And ultimately it should be possible to enforce that all resources are managed, because ultimately all resource interfaces have to be provided by the language runtime. To a certain extent enforcing user-defined invariants is always a matter of taste, but at the same time a language that makes it easy makes people a lot better at it.
GOOG is a big company, and not everyone handles this the same way. At least some teams are doing what the sibling mentioned: A static factory-like method that returns a discriminated union of either T or an error type. It is conceptually similar to std::variant<T, std::error_condition>, but syntactically optimized for this use case.
Why global? Just check if the resource is properly initialized:
std::ofstream file("example.txt");
if (!file.is_open()) return;
// ofstream will close in destructor.
And RAII doesn't even imply that the acquisition must happen via constructor. Edit: Technically it does, but the above pattern would not change if 'file' was a result of calling a free function or whatever.
> Just check if the resource is properly initialized: std::ofstream file("example.txt"); if (!file.is_open()) return;
This is exactly not RAII. You acquired the resource and it might not be initialized. And it has the same problem: it's very easy to pass file around when it's not actually valid.
Yes, this forces you to explicitly check for errors. I find uninitialized objects' badness comparable to the badness of unhandled exceptions - neither is very hard to debug.
But full exception safety is obviously much more than just properly catching exceptions. I mean avoiding subtle traps like the ones here: https://herbsutter.com/gotw/_102/
>I find uninitialized objects' badness comparable to the badness of unhandled exceptions - neither is very hard to debug.
Unless you have a bunch of code that just passes the object around without looking into it (so it won't fail there), and then when the crash finally happens, you have no idea where the invalid object came from.
This is really just another form of the billion dollar mistake, except you have invalid objects instead of null pointers (or in the worst case, you have both).
With an unhandled exception, you always have the context of where and why it happened (assuming you know the reason behind throwing it).
This code suffers from a race condition: the file might be deleted/moved/renamed at any point, even if the file.is_open() check succeeds. Code that does not consider this possibility is technically fragile under a wide set of pretty common conditions.
Windows doesn't allow deleting an open file. Unix allows the deleted file to be used (even for IPC) and doesn't GC it until it's closed. Breaking an open file's user would be bad design requiring some kind of complicated notification system to be at all usable.
The exceptions crash the program. It's helpful and critical to debug the program and to stop it in case of bug/failure.
That said. I never use exceptions per-se. I just happen to get a std::out_of_bound_exception once in a while (for which there is nothing to be done, except crash :D).
Not sure what would happen if exceptions were disabled. Maybe the error handling would be disabled and program would keep running[1]. Maybe the STL would just call abort() instead of throwing an exception[2].
[1] That would be a disaster. Should never disable exception if so.
noexcept is a messier than const because it's not straightforward to assess if code you rely on should be throwing or not since that's an implementation detail versus a contract around data ownership. This makes maintenance hard as implementations change.
It'd be nice to see compilers infer the exception behavior themselves and do these optimizations when appropriate.
It is probably also worth noting that there is a good optimization opportunity when using noexcept move constructors. std::vector::push_back has a strong exception safety guarantee, so the normal behavior when the vector is at capacity is to create a new buffer and copy construct each item in the vector into it. If the copy constructor of any element throws, the original buffer is still in a coherent state.
If the type has a move constructor that is noexcept, then each element can be move-constructed without fear of an exception occurring. For many types, this is vastly more efficient.
My dream exception-related feature would be compile-time verification of noexcept.
I don't care what gets thrown, because it's in the end going to be an std::exception (this is the part that Java messed up)
I do care if a function will throw, because it radically changes its behavior.
The problem I want to avoid is a silly function such as stoi throwing and having the exception propagated up to main, after which the whole program (orderly) shuts down.
Right now this can be avoided only with careful exception design and making sure all exception "escape points" to C functions, message loops, etc are covered.
Swift I think is one of the few languages supporting this.
Not so: The Standard doesn't have anything to say about the differences between static and dynamic libraries, or the differences between a .exe and its .dll's (or .so's). The program is composed of all the things that link into it as far as the ODR is concerned.
The breakage from mixing different library versions (say, MSVCRT versions, for example) is a direct result of violating the ODR.
The standard doesn't say anything at all about dynamic linking AFAIK, so it's basically OS-dependent. I think it works (as in the algorithm is well defined) in Linux, but it's best avoided due to complexity and non-portability.
MSVC does not guarantee a stable standard library ABI, so those that need to target it are out of luck. In linux land, the switch from libstdc++5 to 6 still haunt the memories of many, although it was quite a long time ago (gcc 3.4) and the ABI has been stable since.
In practice the reason that many projects provide a C ABI is that they want to interoperate with other languages and the C ABI, being extremely minimalist, is the least common denominator.
> I don't care what gets thrown, because it's in the end going to be an std::exception (this is the part that Java messed up)
I don't understand: std::exception is entirely optional (and I, for example, never use it: I always build my own exception hierarchy; but, even though I write my native code almost exclusively in C++, I believe it is absolutely incorrect to expose a C++ API from a library, so you'd never know or care) while in Java you absolutely have to implement Throwable and Exception is actually designed well enough that most people (not me, of course ;P) generally use it... I mean, in C++ you literally can just throw a "const char *" and catch that.
By Java mess I meant exception specifications (throws/doesn't throw vs. exception names), sorry for the confusion.
The important thing with exceptions in C++ is having a root class. I use std::exceptions, others are also ok as long as they are used consistently.
If the goal is to trap everything, like in a library even ... is ok.
The big difficulty lies in isolating certain kinds of exceptions to components. Some are recoverable, others should lead to termination, some should interrupt the current operation, others should trigger a retry. The type system offers no support with that.
Verification of noexcept would be a start, as it allows some rudimentary marking of boundaries.
I happen to believe the inverse of all of this. Yes std::exception is optional, but why not use it to root your hierarchy? The only time I've answered in the negative is when the project specifically required no standard library could be used (yes, great that it's optional!). I sympathize with C-compatible API's to simplify an interface, though it's not my first choice. Rooting hierarchies in std::exception integrates library code safely and consistently -- especially over time and code revision to both the library and your end -- because at some point after specific exceptions are all missed some `catch(std::exception)` is hit on errors from both you and the library, and a consistent `what()` call can eventually inform the user with a message.
I will say that I didn't provide an argument for not using std::exception, nor was I trying to convince someone else not to do so; I was simply stating that C++ does not require it and a lot of code doesn't use it: it sounded like the person I was responding to was claiming that in C++ you knew everything was eventually std::exception while that wasn't the case for Java, when the exact opposite is actually true (that in C++ you can and often do run into cases where an exception is just some random data type, and in Java all exceptions fundamentally must implement Throwable as an engine constraint). I only used the fact that I don't use it as an example of someone who doesn't. FWIW, I actually use a lot of my code in environments where I can't use the standard library, so even your paragraph of why you supposedly believe the opposite of some ancillary point I wasn't trying to make you have admitted why I do what I do is valid even to you.
> I sympathize with C-compatible API's to simplify an interface, though it's not my first choice.
It isn't about simplifying an interface: it is because in addition to the C++ ABI being itself fundamentally brittle (due to vtable ordering), you have to remember that some people use libstdc++ while others are now annoyingly using libc++ (I blame Apple, and I have absolutely no sympathy: they have ruined what little interoperability we used to have in this ecosystem they cared absolutely nothing for), and the ABIs between popular compilers is different; so when someone uses a C++ interface for their library I spend the next three days cursing their name and adding them to my "never let this person program anything ever again" list while I waste my time pulling crazy stunts to dynamically look up mangled symbol names and working around egregious incompatibilities in basic things like "when a C++ member function returns a structure, MinGW puts the hidden structure return pointer first while MSVC puts it after the hidden this pointer".
At this point I know the ABI of every major compiler on every major architecture, and I have been considering switching entirely to gcc and contributing a ton of patches that let me #pragma mark entire regions of code (due to header files) with specific calling conventions and layout ABIs, as this attitude that C++ is somehow appropriate for this purpose is creeping into more and more places as C++11 has caused more and more people to start using C++ without understanding its limits :/.
The entire concept of an unending exception chain seems flawed to me, and having to carefully annotate or check everywhere is still error-prone for programmers.
It ought to be possible to create impenetrable exception boundaries, such as:
- A compilation unit / file.
- Something identified as a “library” (perhaps based on namespace).
- The program itself.
If I want my “library” to automatically trap all exceptions that I throw, I should be able to. I shouldn’t have to worry that entire programs may crash because of mistakes from exceptions. Similarly, a programmer should be able to say, ONCE (regardless of any functions added in the future), “trap all exceptions from code in this file using this function” and be done with it.
And, if the language had been designed with this kind of visibility into exceptions, one would also have more useful information at the time they are trapped. For instance, obviously exceptions propagating all the way to main() have very little useful context but knowing that an exception is trapped in a file scope would be huge for debugging.
There are real use cases where you need granular control, like implementing WinRT components on Windows. Basically, anytime you need to have a barrier between error code-based and exception-based code, or at ABI boundaries.
And this is why the people who wrote this code base I'm maintaining have manual stack-tracing code for the application - so whenever an exception is constructed of certain types we can dump its call location into the logs. Otherwise exceptions just appear as weird nonlocal flow control that loses information.
GCC users have backtrace_symbols() for this.
(It was an interesting exercise to see how much of the ARM stack unwinding I could do in C++ rather than assembler; I managed it with only one asm instruction to get the link register and a lot of instruction-decoding C++)
> I shouldn’t have to worry that entire programs may crash because of mistakes from exceptions.
Why? This is the best possible outcome. Continuing to run is the worst possible outcome from a correctness, security, and stability perspective. I can't imagine more of an anti-feature.
Simple: the meaning of the exception is known only to the guts of your library, and it is being automatically promoted to “catastrophic failure” when it could actually be a very trivial problem or at least something that you could recover from gracefully. Instead, because you potentially forgot an exception clause somewhere, my entire program dies in an unpredictable place? That makes the entire model ridiculous.
Almost all exceptions cannot be recovered from gracefully. Those that can, are generally recovered/retried from within your own code not the libraries code.
An unhandled exception should always be a catastrophic error -- exceptions are exactly for situations where the code is in an unexpected situation. If you keep running, the application is now running in that unpredictable state. You just want to bury your head in the sand at a library boundary but I don't see why that's desirable.
A well designed program (that isn't written in Java) should only have a handful of catch statements.
You are making huge assumptions about how people use exceptions, and huge assumptions about the goals of a program.
While you and I may agree that exceptions should be used for unexpected situations, nothing forces people to use them that way. A library programmer may have weird ideas about what warrants an exception. If my application appears to be functioning correctly, I should not have to risk crashing because of something that cannot be proven to affect my program in a significant way. My program may be trying to remain stable (e.g. a GUI for a user), and the user may well not care if my program has encountered an issue if it means throwing out their last 10 minutes of unsaved work. Some programs like CAD tools may run for days, and even partial results with logged errors can still be important to preserve in lieu of stupid crashes.
Right. So you catch the library exception in your code and deal with it. In GUI applications, I usually just have one top-level handler that shows any unhandled exceptions in a dialog and keeps running. Generally that's all a GUI app needs to remain running if it cleans the call stack up correctly and consistently
That's the correct way to do it; blanket ignoring all unhandled exceptions from a library is just very strange by comparison. If there is a specific exception that you want to ignore, by all means ignore it and document it heavily. But you don't know if every exception is irrelevant or potentially data corrupting.
If an exception violates its (function's) contract, it can terminate immediately and not reach even a catch-all. That contract can be inside a function I can't even see.
Even if I do have a catch-all, what tells me that it is a "library" exception? They might have thrown a generic std::exception with a vague string description. They might have thrown an "int". At some point what I really want is proper language and compiler support for those boundaries so I can know exactly where an exception came from, and can trap it at those boundaries (or ask the library maintainers to do so).
This just sounds like there are whole of really bad coding and C++ anti-features and you want to solve it with more C++ anti-features and bad code.
A function could just not use exceptions at all and just call abort() on error, what are you going to do then?
There isn't really a concept of library boundary in C++; you just have compilation units at best. At worst, you have a library that's implemented entirely as headers. What happens when the exception originates in your code but goes through the libraries call stack (because it was a call on a passed-in object or callback)? What you are asking for is likely impossible.
I know exactly what boundaries are lacking, that is what led to my original comment. And yes, functions can do whatever they want. Programmers expect special language features to be more useful than the alternatives though! If a function does call abort(), I can actually see the call, along with a stack trace; with source code I even know why the code exited. Odds are good that the abort() came from an assert() macro, giving me control over which builds can unexpectedly exit. Exceptions give me none of that, and introduce ways for valuable state to be totally lost in the process.
I'd be happy if C++ had nice stack traces with exceptions -- that's a good potential language feature. But adding features to explicitly swallow exceptions doesn't sit right with me.
> While you and I may agree that exceptions should be used for unexpected situations, nothing forces people to use them that way.
Here's another one: C++ allows programmers to overload operators, but doesn't enforce anything with regard to the overloaded behavior. I can make "+" actually subtract, or make "<", "==", and ">" always return "true", or have a function called "add_two" that actually adds three! When you find a language that prevents people from using it incorrectly, please let me know. Until then, I'll have to follow my rule of thumb that a library that crashes randomly -- whether it's caused by SEGFAULTing, failing to follow it's own exception specifications, or for any other reason -- is buggy and I won't use it.
*
In another comment, you say that you would like a language that enforces internal boundaries. It's not clear to me how that would work. Let's use a specific example:
Imagine I am using a garbage collector library, and I ask for memory. It's a copying collector, and my request ends up promoting objects from one generation into another. Unfortunately, more objects survive than expected, so there isn't room in the target generation's pool. The library throws an exception, fails to handle it, and an internal exception specification doesn't include the exception causing us trouble. In other words, the programmer listed which scenarios were handled, and this one was not on the list. And, of course, we have a boundary like you had asked for.
What can cross that boundary? The library doesn't have the memory I asked for. If we pass the exception across the boundary, it's not clear what purpose the boundary serves. It's also not clear what I should do with that exception: it's an internal detail. Even if I can figure out that we essentially have a buffer that's too small, I don't have access to that buffer (so I can't make it any larger), and I probably don't have access to the functions needed to start promoting survivors from our too-small generation to make room. How would the proposed boundaries handle this problem?
A boundary might not be enforceable in an absolute sense but the language should be able to tell when the boundary is crossed. An exception caught in main() that the compiler could automatically tag as having crossed a particular library or compilation unit would be infinitely more useful for debugging than what we have now.
What makes a library sufficiently buggy? The set of conditions leading to a crash from an exception at run time may not be known to the library maintainer, even in a “good” library. The crash in your particular program, caused by the library, may not occur for awhile, after which you already depend on the API. With a normal error-handling scheme, this would still be OK because you could trace the error.
Programs can already crash in enough ways, without C++ introducing more. I can debug even severe memory errors in fairly straightforward ways (e.g. trap in debugger, or run "valgrind", or other instruments). I hate debugging a “crash” that is essentially C++ converting a trivial mistake from someone’s error-handling code into a fatal, untraceable run time error. When I see terminate() I know I have a problem. I essentially have to start using "grep" for every possible "throw" statement (hopefully finding something in recently-committed code), trying to match functions that might be related to what the program seemed to be doing when it died. And since C++ exceptions provide very little help when debugging, one has to go to extra effort to add information to every exception (which is useless if the exception at run time didn’t come from your own code).
C++ exceptions are just not “better”, like a lot of C++ changes. They can’t break crucial debugging mechanisms like stack-traces and expect the result to be better.
There's a surprising number of people still relying on printf debugging. With distributed systems the slightly more advanced version: log based debugging is getting even more common.
I realized after I posted my last question that a language could convert an unexpected exception into some other notice that "something went wrong" (e.g., another exception). But if that behavior is ever triggered, it's not clear what you can do. You have a library reporting that (1) it detected an internal problem AND (2) it did not handle that problem. Any objects you have from that library might be affected, so you can't reliably call functions on them. If the library provides a way to deallocate its objects ( https://blogs.msdn.microsoft.com/oldnewthing/20060915-04/?p=... ), you can't trust that it will work. Letting the objects fall out of scope isn't even safe.
By the way, you can get very close to this behavior today with `std::set_unexpected` ( http://www.cplusplus.com/reference/exception/set_unexpected/ ). That page says that the function you set as the unexpected handler must "end either by terminating (calling `terminate` or some other method, such as `exit` or `abort`) or by throwing an exception (even rethrowing the same exception again). If the exception thrown (or rethrown) is not in the function's dynamic-exception-specification but `bad_exception` is, a `bad_exception` is thrown" (emphasis added).
*
> Programs can already crash in enough ways, without C++ introducing more. ... When I see terminate() I know I have a problem. I essentially have to start using "grep" for every possible "throw" statement (hopefully finding something in recently-committed code), trying to match functions that might be related to what the program seemed to be doing when it died. And since C++ exceptions provide very little help when debugging, one has to go to extra effort to add information to every exception (which is useless if the exception at run time didn’t come from your own code).
For the record, I'm not really a fan of exceptions or fancy error handling code. I subscribe to the school of thought that you probably have a main loop (which handles requests, or waits for user input, etc.) and that loop should have your single try/catch block ( http://www.artima.com/intv/handcuffsP.html ). You handle exceptions by giving up on what you were trying to do, and perhaps letting the user know. You then go to the next loop iteration.
That is, I almost never see a need for two try/catch blocks in a program ( https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexi... ). Error handling code is especially notorious for not being tested and therefore not doing what it should. Therefore, error handling code beyond "abandon the current request" is fishy to me.
And I agree that one of the few things I miss when I work in C++ is exceptions that carry a lot of information automatically. In C++, if you want the exception to carry file/line number information, you have to add it yourself (probably via a macro: "#define THROW_EX(X) throw AppException((X), __FILE__, __LINE__)" or something similar). And that doesn't even address nested exceptions. I rely on a lot of logging, and the ability to reliably reproduce the problem while running it in a debugger.
I believe the Committee, so far, has avoided bringing exceptions on par with you see in other languages because doing so would have an unusually high execution and/or implementation cost compared to other aspects of C++. It's entirely p...
Haha, tell that to the user who did not get his work saved because the programming language terminates the program. All because the programmer made a mistake of defining code noexcept when in reality an exception was thrown.
noexcept is a bullshit feature if you care about stability
These are my uses for unconditional noexcept, they are not all encompassing but they work for me, in all cases the exception guarantee of the code should be noted:
* Code that will never throw an exception
* Code where throwing an unanticipated and therefore uncaught exception should trigger a failfast and crashing is not only the right behavior but the desired behavior. This should be the rarest of cases, but it does happen.
* Code that is called across an ABI boundary that it is not safe for an exception to cross. This is the most common case as OS callbacks fit this. This should be noexcept because having the exception propagate across exception unaware code causes bad things to happen, it's also undefined behavior to boot.
86 comments
[ 2.7 ms ] story [ 160 ms ] threadI didn't follow the development of noexcept, so I can only guess at the motives. By and large, there were three complaints about exception specifications that I am familiar with: (1) by requiring stack unwinding when a no-throw specification was violated, they often caused worse performance (or, at least, didn't allow an optimization opportunity); (2) generally, the important question is whether a function might throw, not which exceptions it might throw; and (3) the rules around when exception specifications affect a function's signature -- e.g., does a pointer to a function have to match the specification exactly? -- were convoluted. I think it was reasonable for the Committee to focus on those issues.
Existing unmarked functions would be expeced to possibly throw everything, while functions with throw specifications would be statically checked.
You can call the later from the former, but to do the reverse you need an explicit try/catch. Some form of throw(auto) would enable automatic deduction of exception specifications for e.g. template code.
Dyanamic exception specifications will be removed fron the language in c++17, so the synax is open to be reused for such a feature in the future.
In the 3+ years I'm working in C++ without exceptions, I haven't missed them once.
When I first used Qt, I wondered how such a large framework could get along without exceptions. After writing some non-trivial Qt programs, I still noticed that there is really no need for exceptions, except when interfacing non-Qt libraries, where I simply caught and handled the exceptions in a small wrapper.
Finally, I got it: Qt uses the Null-Object pattern (not to be confused with Null values!) consistently, which is really in almost all cases the more elegant solution:
https://en.wikipedia.org/wiki/Null_Object_pattern
This is probably the only sane alternative to exceptions - optional types and result<T, E> types.
> We do not use C++ exceptions.
https://google.github.io/styleguide/cppguide.html#Exceptions
http://llvm.org/docs/CodingStandards.html#do-not-use-rtti-or...
And the destructor has to check for initialization before cleanup.
I'd be very suspicious of any failure avoidance system that relies on "user caution".
Now, true, some of these impose lesser burdens than others. Arguably, RAII imposes the least - but not zero. And if you forget with any resource, for that resource you are vulnerable.
And ultimately it should be possible to enforce that all resources are managed, because ultimately all resource interfaces have to be provided by the language runtime. To a certain extent enforcing user-defined invariants is always a matter of taste, but at the same time a language that makes it easy makes people a lot better at it.
Objects with an artificial invalid state are an antipattern in idiomatic c++.
This is exactly not RAII. You acquired the resource and it might not be initialized. And it has the same problem: it's very easy to pass file around when it's not actually valid.
But full exception safety is obviously much more than just properly catching exceptions. I mean avoiding subtle traps like the ones here: https://herbsutter.com/gotw/_102/
Unless you have a bunch of code that just passes the object around without looking into it (so it won't fail there), and then when the crash finally happens, you have no idea where the invalid object came from.
This is really just another form of the billion dollar mistake, except you have invalid objects instead of null pointers (or in the worst case, you have both).
With an unhandled exception, you always have the context of where and why it happened (assuming you know the reason behind throwing it).
use abort() from stdlib.h
The exceptions crash the program. It's helpful and critical to debug the program and to stop it in case of bug/failure.
That said. I never use exceptions per-se. I just happen to get a std::out_of_bound_exception once in a while (for which there is nothing to be done, except crash :D).
Not sure what would happen if exceptions were disabled. Maybe the error handling would be disabled and program would keep running[1]. Maybe the STL would just call abort() instead of throwing an exception[2].
[1] That would be a disaster. Should never disable exception if so.
It'd be nice to see compilers infer the exception behavior themselves and do these optimizations when appropriate.
If the type has a move constructor that is noexcept, then each element can be move-constructed without fear of an exception occurring. For many types, this is vastly more efficient.
I don't care what gets thrown, because it's in the end going to be an std::exception (this is the part that Java messed up)
I do care if a function will throw, because it radically changes its behavior.
The problem I want to avoid is a silly function such as stoi throwing and having the exception propagated up to main, after which the whole program (orderly) shuts down. Right now this can be avoided only with careful exception design and making sure all exception "escape points" to C functions, message loops, etc are covered.
Swift I think is one of the few languages supporting this.
Object layouts will be likely different and the main program will probably crash.
The breakage from mixing different library versions (say, MSVCRT versions, for example) is a direct result of violating the ODR.
Make sure that your compiler provides a stable std library ABI.
Is this done a lot in practice? I had the impression that a lot of projects stick to C APIs in dynamic libraries.
In practice the reason that many projects provide a C ABI is that they want to interoperate with other languages and the C ABI, being extremely minimalist, is the least common denominator.
I don't understand: std::exception is entirely optional (and I, for example, never use it: I always build my own exception hierarchy; but, even though I write my native code almost exclusively in C++, I believe it is absolutely incorrect to expose a C++ API from a library, so you'd never know or care) while in Java you absolutely have to implement Throwable and Exception is actually designed well enough that most people (not me, of course ;P) generally use it... I mean, in C++ you literally can just throw a "const char *" and catch that.
The important thing with exceptions in C++ is having a root class. I use std::exceptions, others are also ok as long as they are used consistently. If the goal is to trap everything, like in a library even ... is ok.
The big difficulty lies in isolating certain kinds of exceptions to components. Some are recoverable, others should lead to termination, some should interrupt the current operation, others should trigger a retry. The type system offers no support with that.
Verification of noexcept would be a start, as it allows some rudimentary marking of boundaries.
I will say that I didn't provide an argument for not using std::exception, nor was I trying to convince someone else not to do so; I was simply stating that C++ does not require it and a lot of code doesn't use it: it sounded like the person I was responding to was claiming that in C++ you knew everything was eventually std::exception while that wasn't the case for Java, when the exact opposite is actually true (that in C++ you can and often do run into cases where an exception is just some random data type, and in Java all exceptions fundamentally must implement Throwable as an engine constraint). I only used the fact that I don't use it as an example of someone who doesn't. FWIW, I actually use a lot of my code in environments where I can't use the standard library, so even your paragraph of why you supposedly believe the opposite of some ancillary point I wasn't trying to make you have admitted why I do what I do is valid even to you.
> I sympathize with C-compatible API's to simplify an interface, though it's not my first choice.
It isn't about simplifying an interface: it is because in addition to the C++ ABI being itself fundamentally brittle (due to vtable ordering), you have to remember that some people use libstdc++ while others are now annoyingly using libc++ (I blame Apple, and I have absolutely no sympathy: they have ruined what little interoperability we used to have in this ecosystem they cared absolutely nothing for), and the ABIs between popular compilers is different; so when someone uses a C++ interface for their library I spend the next three days cursing their name and adding them to my "never let this person program anything ever again" list while I waste my time pulling crazy stunts to dynamically look up mangled symbol names and working around egregious incompatibilities in basic things like "when a C++ member function returns a structure, MinGW puts the hidden structure return pointer first while MSVC puts it after the hidden this pointer".
At this point I know the ABI of every major compiler on every major architecture, and I have been considering switching entirely to gcc and contributing a ton of patches that let me #pragma mark entire regions of code (due to header files) with specific calling conventions and layout ABIs, as this attitude that C++ is somehow appropriate for this purpose is creeping into more and more places as C++11 has caused more and more people to start using C++ without understanding its limits :/.
It ought to be possible to create impenetrable exception boundaries, such as:
- A compilation unit / file.
- Something identified as a “library” (perhaps based on namespace).
- The program itself.
If I want my “library” to automatically trap all exceptions that I throw, I should be able to. I shouldn’t have to worry that entire programs may crash because of mistakes from exceptions. Similarly, a programmer should be able to say, ONCE (regardless of any functions added in the future), “trap all exceptions from code in this file using this function” and be done with it.
And, if the language had been designed with this kind of visibility into exceptions, one would also have more useful information at the time they are trapped. For instance, obviously exceptions propagating all the way to main() have very little useful context but knowing that an exception is trapped in a file scope would be huge for debugging.
If you need more isolation, maybe you can make a design based on restartable servers.
GCC users have backtrace_symbols() for this.
(It was an interesting exercise to see how much of the ARM stack unwinding I could do in C++ rather than assembler; I managed it with only one asm instruction to get the link register and a lot of instruction-decoding C++)
Why? This is the best possible outcome. Continuing to run is the worst possible outcome from a correctness, security, and stability perspective. I can't imagine more of an anti-feature.
An unhandled exception should always be a catastrophic error -- exceptions are exactly for situations where the code is in an unexpected situation. If you keep running, the application is now running in that unpredictable state. You just want to bury your head in the sand at a library boundary but I don't see why that's desirable.
A well designed program (that isn't written in Java) should only have a handful of catch statements.
While you and I may agree that exceptions should be used for unexpected situations, nothing forces people to use them that way. A library programmer may have weird ideas about what warrants an exception. If my application appears to be functioning correctly, I should not have to risk crashing because of something that cannot be proven to affect my program in a significant way. My program may be trying to remain stable (e.g. a GUI for a user), and the user may well not care if my program has encountered an issue if it means throwing out their last 10 minutes of unsaved work. Some programs like CAD tools may run for days, and even partial results with logged errors can still be important to preserve in lieu of stupid crashes.
That's the correct way to do it; blanket ignoring all unhandled exceptions from a library is just very strange by comparison. If there is a specific exception that you want to ignore, by all means ignore it and document it heavily. But you don't know if every exception is irrelevant or potentially data corrupting.
Even if I do have a catch-all, what tells me that it is a "library" exception? They might have thrown a generic std::exception with a vague string description. They might have thrown an "int". At some point what I really want is proper language and compiler support for those boundaries so I can know exactly where an exception came from, and can trap it at those boundaries (or ask the library maintainers to do so).
A function could just not use exceptions at all and just call abort() on error, what are you going to do then?
There isn't really a concept of library boundary in C++; you just have compilation units at best. At worst, you have a library that's implemented entirely as headers. What happens when the exception originates in your code but goes through the libraries call stack (because it was a call on a passed-in object or callback)? What you are asking for is likely impossible.
Here's another one: C++ allows programmers to overload operators, but doesn't enforce anything with regard to the overloaded behavior. I can make "+" actually subtract, or make "<", "==", and ">" always return "true", or have a function called "add_two" that actually adds three! When you find a language that prevents people from using it incorrectly, please let me know. Until then, I'll have to follow my rule of thumb that a library that crashes randomly -- whether it's caused by SEGFAULTing, failing to follow it's own exception specifications, or for any other reason -- is buggy and I won't use it.
*
In another comment, you say that you would like a language that enforces internal boundaries. It's not clear to me how that would work. Let's use a specific example:
Imagine I am using a garbage collector library, and I ask for memory. It's a copying collector, and my request ends up promoting objects from one generation into another. Unfortunately, more objects survive than expected, so there isn't room in the target generation's pool. The library throws an exception, fails to handle it, and an internal exception specification doesn't include the exception causing us trouble. In other words, the programmer listed which scenarios were handled, and this one was not on the list. And, of course, we have a boundary like you had asked for.
What can cross that boundary? The library doesn't have the memory I asked for. If we pass the exception across the boundary, it's not clear what purpose the boundary serves. It's also not clear what I should do with that exception: it's an internal detail. Even if I can figure out that we essentially have a buffer that's too small, I don't have access to that buffer (so I can't make it any larger), and I probably don't have access to the functions needed to start promoting survivors from our too-small generation to make room. How would the proposed boundaries handle this problem?
What makes a library sufficiently buggy? The set of conditions leading to a crash from an exception at run time may not be known to the library maintainer, even in a “good” library. The crash in your particular program, caused by the library, may not occur for awhile, after which you already depend on the API. With a normal error-handling scheme, this would still be OK because you could trace the error.
Programs can already crash in enough ways, without C++ introducing more. I can debug even severe memory errors in fairly straightforward ways (e.g. trap in debugger, or run "valgrind", or other instruments). I hate debugging a “crash” that is essentially C++ converting a trivial mistake from someone’s error-handling code into a fatal, untraceable run time error. When I see terminate() I know I have a problem. I essentially have to start using "grep" for every possible "throw" statement (hopefully finding something in recently-committed code), trying to match functions that might be related to what the program seemed to be doing when it died. And since C++ exceptions provide very little help when debugging, one has to go to extra effort to add information to every exception (which is useless if the exception at run time didn’t come from your own code).
C++ exceptions are just not “better”, like a lot of C++ changes. They can’t break crucial debugging mechanisms like stack-traces and expect the result to be better.
What debugger are you using that doesn't provide a full stack trace on an uncaught exception?
By the way, you can get very close to this behavior today with `std::set_unexpected` ( http://www.cplusplus.com/reference/exception/set_unexpected/ ). That page says that the function you set as the unexpected handler must "end either by terminating (calling `terminate` or some other method, such as `exit` or `abort`) or by throwing an exception (even rethrowing the same exception again). If the exception thrown (or rethrown) is not in the function's dynamic-exception-specification but `bad_exception` is, a `bad_exception` is thrown" (emphasis added).
*
> Programs can already crash in enough ways, without C++ introducing more. ... When I see terminate() I know I have a problem. I essentially have to start using "grep" for every possible "throw" statement (hopefully finding something in recently-committed code), trying to match functions that might be related to what the program seemed to be doing when it died. And since C++ exceptions provide very little help when debugging, one has to go to extra effort to add information to every exception (which is useless if the exception at run time didn’t come from your own code).
For the record, I'm not really a fan of exceptions or fancy error handling code. I subscribe to the school of thought that you probably have a main loop (which handles requests, or waits for user input, etc.) and that loop should have your single try/catch block ( http://www.artima.com/intv/handcuffsP.html ). You handle exceptions by giving up on what you were trying to do, and perhaps letting the user know. You then go to the next loop iteration.
That is, I almost never see a need for two try/catch blocks in a program ( https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexi... ). Error handling code is especially notorious for not being tested and therefore not doing what it should. Therefore, error handling code beyond "abandon the current request" is fishy to me.
And I agree that one of the few things I miss when I work in C++ is exceptions that carry a lot of information automatically. In C++, if you want the exception to carry file/line number information, you have to add it yourself (probably via a macro: "#define THROW_EX(X) throw AppException((X), __FILE__, __LINE__)" or something similar). And that doesn't even address nested exceptions. I rely on a lot of logging, and the ability to reliably reproduce the problem while running it in a debugger.
I believe the Committee, so far, has avoided bringing exceptions on par with you see in other languages because doing so would have an unusually high execution and/or implementation cost compared to other aspects of C++. It's entirely p...
noexcept is a bullshit feature if you care about stability
A program that loses user data on a crash (any crash), is just not designed for reliability, period.
A reliable program will probably use transactional storage and periodic checkpoints.
Look-up "crash only software".
Uh huh. And how do you feel about sefaults?
* Code that will never throw an exception
* Code where throwing an unanticipated and therefore uncaught exception should trigger a failfast and crashing is not only the right behavior but the desired behavior. This should be the rarest of cases, but it does happen.
* Code that is called across an ABI boundary that it is not safe for an exception to cross. This is the most common case as OS callbacks fit this. This should be noexcept because having the exception propagate across exception unaware code causes bad things to happen, it's also undefined behavior to boot.