I'm working on old code that relies heavily on the exception specifications behavior described in the language standard. Namely, calls to std::unexpected() on exception specification violations of the form described below.
foo() throw(T) { /*...*/ }
Nothrow specifications are indeed guaranteed to not throw, but throw(T) ones are expected to be violated both by design and... well, because the standard expects as much and provides a mechanism to handle it.
The reasons for this are tied to the designers decision of using EH also as an error handling mechanism (controlled by its own error class hierarchy) in addition to exception handling. The idiom presented in EH closely mapped to their needs and they took the path of least effort. This is at least how I see it and isn't particularly shocking to me, given the size and complexity of the system.
I'm however now tasked to include new and unrelated functionality and the code isn't behaving as expected under VC++ 9.0, due to the deviation from the standards regarding exception specifications introduced in 8.0. (reference: Microsoft)
I'm trying to find a way to force the standard behavior. Was hoping for a fallback to be offered by the compiler. But there is none.
Am I out of luck and need to change correctly written standard-obedient code running on the 350,000 lines of code with a fully developed error handling class hierarchy? Or can you think of a way that will help me to force std::unexpected() behavior?
EDIT:
I'm providing some background information. The system in question is a School Year Calendars Generator for a school serving a little over 4,000 students distributed among, I'm unsure as to some of the numbers yet, 6 grades and ~190 classes, plus 12 virtual (long-distance teaching) classes. MINGW is out of the question as is any compiler other than VC++ 8.0 or 9.0. This is due to regulations pertaining to software serving the Educational System in this country.
The changes needed to the code are exactly to accommodate the introduction of the virtual classes with a vastly different schema for calendar generation. And then I bumped into this problem. The software makes heavy use of the exceptions mechanism on a few parts of the calendar generation process as a means to control workflow through both unexpected() mappings (saved and restored) and bad_exception mappings, none of which work under VC++. On a purely personal note, I find the mechanism in place actually very elegant even if entirely uncommon. But I digress.
I don't believe that Visual C++ exception specification behaviour has ever been (or claimed to have been) standards conforming - even before 8.0 - so I'm not sure how the application has been working.
Is it feasible to perform changes such as:
void f() throw(T)
{
// ...
}
to:
void f()
{
try
{
// ...
}
catch (T)
{
throw;
}
catch (...)
{
app_unexpected();
}
}
As you mentioned, Visual Studio has an "interesting" way of dealing with exception specifications:
throw() has its normal meaning (the function must not throw)
anything else (including no exception specification) is interpreted as throw(...)
There is no way to circumvent this. However, the C++ community pretty much agrees that exception specifications are useless. Do you really need runtime checking of error types thrown? Perhaps proper unit testing can replace your runtime checks.
Related
I thought this would sound a general simple question but I got up this when reading C++ exception specification. that said in one of the book, C++11 now have a keyword 'noexcept' that means no exception will be thrown from a function when it is declared with the function header and that said the reason for this keyword came into existence is C++ exception specifications are checked at run time rather than at compile time, so they offer no programmer guarantees that all exceptions have been handled. and hence they conclude two case a function would throw exception or if we are clear if it will never throw, then use noexcept for optimization(hopefully)
void foo() noexcept();
Here is the main question. Which system software perform those run time checking(I hope not compiler/linker/loader) and also which system software is responsible for allocating memory at run time(dynamic memory allocation) when this are all not taken care by compiler and others?
There is no active "system software" checking for exceptions, as you phrase it; rather, throwing an exception is an action taken by the program itself. The program passes the exception back up the stack until the exception matches an exception handler.
If no exception handler matches, then the exception is caught by the bootstrap code (main is not the actual entry point for a typical program, but is where the runtime hands control to the programmer) and the program terminates.
AFAIK this is done by the C++ runtime (libstdc++ for example). In case of exceptions, there are some guards added around the functions by the compiler (this is necessary anyway to call destructors in case exception is thrown), and in the case of noexcept, if the function throws (or if it throws other exception than advertised by the throw() specification), terminate() is called by the C++ runtime and the application is shut down.
Memory heap allocations are also (by default) done by the C++ runtime libraries.
Typically the responsible software isn't one clearly identifiable piece of code, but small fragments of code sprinkled through the executable. The compiler translates your code into binary instructions, and noexcept is no exception ;).
Indeed, you would not say that the "standard library" handles this. Exceptions and exception specifications are rather a core language feature, more fundamental than the standard library.
You could similarly ask, what piece of software ensures that when I call a function in C++, that the caller actually receives the values that I pass in? What piece of software manipulates the stack frame pointers while my program is running?
From the point of view of the standard I would say "the implementation" is responsible for these details. In some languages, like Java for instance, there is a "Java Runtime Environment" which is very clearly responsible for these things, and you could try to study exactly how it does them. In C++ there is no universal runtime environment -- like others have said, the compiler is responsible to generate code that ensures that these things happen, and that code ends up sprinkled throughout your resulting executable. How exactly the compiler achieves its task is implementation-specific, you can't give a general answer beyond what the standard says, and generally it specifies the expected behavior, not the details under the hood.
When you ask
also which system software is responsible for allocating memory at run time(dynamic memory allocation
this is again an implementation detail, it will differ from compiler to compiler.
Can a C++ exception come "out of nowhere"? Not literally. Does anybody know non obvious special cases when C++ runtime can throw exception from a place that is not an explicit call of the function?
Platform specific experiences and information about implementations that deviate from the standard are interesting also.
Please do not post answers about:
Macroprocessor tricks that hide function calls;
Default constructors;
Destructors;
Overloaded operators;
Overloaded conversions;
Unoverloaded operators new and new[];
MSVC provides an option that allows to handle hardware exceptions (GPF, division by zero, etc) as C++ exceptions. Can anybody comment on how this is handled on other platforms or maybe somebody knows a right place in the standard that speaks about this?
To the guy who downvoted this question: Please, have courage to tell what is wrong here.
No, no, and there isn't one, it's only an MSVC option. Only Windows treats hardware errors like exceptions, and these structured exceptions are only converted into C++ exceptions if you ask for it. The other platforms use signals.
Exceptions do not come out of nowhere. They come when you, or the well-defined places in the Standard library (and a couple in the language like dynamic_cast) throw them. The C++ runtime does not throw them for lolsies. If you have an exception of unknown source, then get a better debugger and learn how to use it until it doesn't have an unknown source.
Google's C++ style guide says "We do not use exceptions". The style does not mention STL with respect to usage of exception. Since STL allocators can fail, how do they handle exceptions thrown by containers?
If they use STL, how is the caller informed of allocation failures? STL methods like push_back() or map operator[] do not return any status codes.
If they do not use STL, what container implementation do they use?
They say that they don't use exceptions, not that nobody should use them. If you look at the rationale they also write:
Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions.
The usual legacy problem. :-(
We simply don't handle exceptions thrown by containers, at least in application-level code.
I've been an engineer at Google Search working in C++ since 2008. We do use STL containers often. I cannot personally recall a single major failure or bug that was ever traced back to something like vector::push_back() or map::operator[] failing, where we said "oh man, we have to rewrite this code because the allocation could fail" or "dang, if only we used exceptions, this could have been avoided." Does a process ever run out of memory? Yes, but this is usually a simple mistake (e.g., someone added a large new data file to the program and forgot to increase the RAM allocation) or a catastrophic failure where there's no good way to recover and proceed. Our system already manages and restarts jobs automatically to be robust to machines with faulty disks, cosmic rays, etc., and this is really no different.
So as far as I can tell, there is no problem here.
I'm pretty sure that they mean they do not use exceptions in their code. If you check out their cpplint script, it does check to ensure you are including the correct headers for STL containers (like vector, list, etc).
I have found that Google mentions this explicitly about STL and exceptions (emphasis is mine):
Although you should not use exceptions in your own code, they are used
extensively in the ATL and some STLs, including the one that comes
with Visual C++. When using the ATL, you should define
_ATL_NO_EXCEPTIONS to disable exceptions. You should investigate whether
you can also disable exceptions in your STL, but if not, it is
OK to turn on exceptions in the compiler. (Note that this is only to
get the STL to compile. You should still not write exception handling
code yourself.)
I don't like such decisions (lucky that I am not working for Google), but they are quite clear about their behaviour and intentions.
You can’t handle allocation failures anyway on modern operating systems; as a performance optimization, they typically over-commit memory. For instance, if you call malloc() and ask for a really huge chunk of memory on Linux, it will succeed even if the memory required to back it actually isn’t there. It’s only when you access it that the kernel actually tries to allocate pages to back it, and at that point it’s too late to tell you that the allocation failed anyway.
So:
Except in special cases, don’t worry about allocation failures. If the machine runs out of memory, that’s a catastrophic failure from which you can’t reliably recover.
Nevertheless, it’s good practice to catch unhandled exceptions and log the e.what() output, then re-throw, since that may be more informative than a backtrace, and typical C++ library implementations don’t do that automatically for you.
The whole huge thread above about how you can’t rely on crashing when you run out of memory is complete and utter rubbish. The C(++) standard may not guarantee it, but on modern systems crashing is the only thing you can rely on if you run out of memory. In particular, you can’t rely on getting a NULL or indeed any other indication from your allocator, up to and include a C++ exception.
If you find yourself on an embedded system where page zero is accessible, I strongly suggest you fix that by mapping an inaccessible page at that location. Human beings cannot be relied upon to check for NULL pointers everywhere, but you can fix that by mapping a page once rather than trying to correct every possible (past, present and future) location at which someone might have missed a NULL.
I will qualify the above by saying that it’s possible you’re using some kind of custom allocator, or that you’re on a system that doesn’t over-commit (embedded systems without swap are one example of that, but not the only example). In that case, maybe you can handle out-of-memory conditions gracefully, on your system. But in general in the 21st century I’m afraid you are unlikely to get the chance; the first you’ll know that your system is out of memory is when things start crashing.
Stl itself is directly only throwing in case of memory allocation failure. But usually a real world application can fail for a variety of reasons, memory allocation failure just one of them. On 32 bit systems memory allocation failure is not something which should be ignored, as it can occur. So the entire discussion above that memory allocation failure is not going to happen is kind of pointless. Even assuming this, one would have to write ones code using two step initialization. And C++ exception handling predates 64 bit architectures by a long time.
I'm not certain how far I should go in dignifying the negative professionalism shown here by google and only answer the question asked. I remember some paper from IBM in around 1997 stating how well some people at IBM understood & appreciated the implications of C++ Exception Handling. Ok professionalism is not necessary
an indicator of success.
So giving up exception handling is not only a problem if one uses STL. It is a problem if one uses C++ as such. It means giving up on
constructor failure
being able to use member objects and base class objects as arguments for any of the following base/member class constructors ( without any testing). It is no wonder that people used two step construction before C++ exception handling existed.
giving up on hierarchical & rich error messages in an environment which allows for code to be provided by customers or third parties and throw errors, which the original writer of the calling code could not possible have foreseen when writing the calling code and could have provided space for in his return error code range.
avoids such hacks as returning a pointer to a static memory object to message allocation failure which the authors of FlexLm did
being able to use a memory allocator returning addresses into a memory mapped sparse file. In this case allocation failure happens when one accesses the memory in question.(ok currently this works only on windows but apple forced the gnu team to provide the necessary functionality in the G++ compiler. Some more pressure from Linux g++ developer will be necessary to provide the this functionality also for them) (oops -- this even applies to STL)
being able to leave this C style coding behind us (ignoring return values) and having to use a debugger with debug executable to find out what is failing in a non trivial environment with child processes and shared libraries provided by third parties or doing remote execution
being able to return rich error information to the caller without just dumping everything to stderr
There is only one possibility to handle allocation failure under assumptions outlined in the question:
that allocator force application exit on allocation failure. In particular, this requires the cusror allocator.
Index-out-of-bound exceptions are less interesting in this context,
because application can ensure they won't happen using pre-checks.
Late to the party here although I didn't see any comparisons between C++ exception handling at Google and exception handling in Go. Specifically, Go only has error handling via a built-in error type. The linked Golang blog post explicitly concludes
Proper error handling is an essential requirement of good software. By employing the techniques described in this post you should be able to write more reliable and succinct Go code.
The creation of Golang certainly took considerations of best practices from working with C++ into account. The underlying intuition is that less can be more. I haven't worked at Google but do find their use of C++ and creation of Golang to be potentially suggestive of underlying best practices at the company.
I often hear the C++ exception system can be disabled as you should not pay for what you do not use. If I choose to compile my C++ program without exceptions will it result in undefined behavior?
The current (and future) C++ standard has no notion of turning off exceptions. So technically yes, doing so leads to undefined behavior, if you ask the language lawyers. Realistically implementations try to define reasonable behavior for this popular extension. Consult your documentation.
To add to Howard's answer.
The general issue is that code written with exceptions in mind could suddenly become bug-riddled if you turn them off.
A simple call to new for example, is supposed to throw the std::bad_alloc exception if the memory request cannot be honored. In case you turn off exceptions it'll return 0 instead: are you sure that all calls to new check that the return is not 0 ? Even those in the standard library or 3rd party libraries ?
CLang recently introduced a diagnosis that prevents compilation if throw or try are used when compiling with exceptions disabled, because it means that the code has not been properly prepared, but I am unsure wrt the other compilers, so watch out.
i am reading this page http://www.cplusplus.com/doc/tutorial/exceptions.html
it says if i write function() throw(); no exceptions can be thrown in that function. I tried in msvc 2005 writing throw(), throw(int), throw() and nothing at all. each had the exact same results. Nothing. I threw int, char*, another type and it was all caught the same way. It looks like throw doesnt affect it at all. What does function() throw() actually do?
See this article for details on C++ exception specifications and Microsoft's implementation:
Microsoft Visual C++ 7.1 ignores exception specifications unless they are empty. Empty exception specifications are equivalent to __declspec(nothrow), and they can help the compiler to reduce code size.
[...] If it sees an empty exception specification, it will assume you know what you are doing and optimize away the mechanics for dealing with exceptions. If your function throws anyway - well, shame on you. Use this feature only if you are 100% positive your function does not throw and never will.
What you're finding is that that version of VC++ didn't enforce specification exceptions. I believe that was documented as a variance from the standard.
However, exception specifications are usually not a good idea. If a program violates them in a standards-conforming implementation (which the VC++ from VS 2005 was not in this case), the system is supposed to catch it. This means that the specification is not a compiler optimization hint, but rather forces the compiler to go to extra lengths, and sometimes produce suboptimal code.
See the Boost rationale for reasons why the highly regarded Boost project does not use exception specifications. This is Boost, which is something of the poster child for doing weird and useful things with advanced parts of the language.
Quoting from A Pragmatic Look at Exception Specifications:
(Mis)understandings
The second issue has to do with
knowing what you’re getting. As many
notable persons, including the authors
of the Boost exception specification
rationale, have put it,
programmers tend to use exception
specifications as though they behaved
the way the programmer would like,
instead of the way they actually do
behave.
Here’s what many people think that
exception specifications do:
Guarantee that functions will only throw listed exceptions (possibly
none).
Enable compiler optimizations based on the knowledge that only listed
exceptions (possibly none) will be
thrown.
The above expectations are, again,
deceptively close to being correct.
See the link for the full details.
Throwing an exception is not enough, you need a try {} catch() block to catch exceptions. If you don't catch exceptions, std::terminate() is called and your program exits abruptly. Take some time out and have go at this.
throw specifications are designed for two purposes:
To serve as a contract between interface implemented and interface user - you state which exceptions can be throwned from your method, some people consider it part of an interface. (contract) Ala checked exceptions in Java.
As a way to signal the compiler that it can apply certain optimizations in case no exceptions can be thrown from a method/procedure (setting up exception handling costs something)
Throwing an exception not specified in throw() clause is a mistake, however at no point is the implementation required to verify it for you. In fact it's not even possible to verify, as it includes all the possible exceptions from subroutines your subroutine calls. (possibly from other modules) It is not even possible within a single module, as is easily reduced to a halting problem :)