1. Unexpected () implementation is inherently restricted
Last time, I introduced the C standard runtime function UNEXPECTED () and showed the restrictions in the implementation of Visual C . This time, I want to show all Unexpected () implementation, and the way to circumvent them.
1.1 Anomaly handling function is global, universal
I have made this at the last time, and then promote a point: filtering UNEXPECTED anomalous anomaly handler UNEXPECTED () is global, unique to each program.
All UNEXPECTED exceptions are processed by the same unExpected () exception handler. The standard runtime provides the default handler to process all UNEXPECTEDs. You can overwrite it with your own version, at this time, the Runtime will call the process you provide to process all UNEXPECTEDs.
And ordinary abnormal processing functions, such as:
Catch (int)
{
}
Different, UNEXPECTED exception handler does not "capture" exception. Once you have entered it, it knows that there is unExpected exception to be thrown, but do not know the type and cause, and even can't get the help of the run library: There is no program or object in the Runbore to save these hateful exceptions.
In the best case, UNEXPECTED exception handler can give control to other parts of the program, perhaps they have a better way. E.g:
#include
Using namespace std;
void my_unexpected_handler ()
{
Throw 1;
}
Void f () throw (int)
{
Throw 1L; // OOPS - * BAD * FUNCTION
}
int main ()
{
Set_unexpected (my_unexpected_handler);
Try
{
f ();
}
Catch (...)
{
}
Return 0;
}
f () throws an exception that it promises does not throw, so MY_UNEXPECTED_HANDLER () is called. This handler does not have any way to determine the reason it is entered. In addition to ending the program, the only way that may be somewhat use is to throw another exception, hoping that the new exception has declares an exceptional specifications of the old abnormal violation, and other parts of the program will capture this new exception.
In this example, my_unexpected_handler () throwing Int exceptions satisfies the abnormal specifications of old abnormal violations, and main () successfully captured it. But slightly change:
#include
Using namespace std;
void my_unexpected_handler ()
{
Throw 1;
}
Void f () throw (char)
{
Throw 1L; // OOPS - * BAD * FUNCTION
}
int main ()
{
Set_unexpected (my_unexepected_handler);
Try
{
f ();
}
Catch (...)
{
}
Return 0;
}
MY_UNEXPECTED_HANDLER () is still called after UNEXPECTED anomaly and still throws an INT type. Unfortunately, INT anomaly is now violated by an old abnormality violates. Therefore, we now violate the same abnormal standard declaration: the first time f (), the second is F () aid MY_UNEXPECTED_HANDLER ().
1.2 Terminate
Now, the program gives up, and calls the runtime program TERMINATE () self-destructive. The TERMINATE () function is the last line of defense on the standard runtime. When the exception handling system of the program is expected, the C standard requires the program to call the Terminate () function. Subclause 15.5.1 of the C standard lists the case of calling terminate (): Like the unExpected () handler, the Terminate () handler can also be defined by the user. However, the updated function is different from the UNEXPECTED () handler must end the program. Remember: When your Terminate () handler is entered, the exception handling system is invalid. This is the last thing you need to find a TERMINATE () handler to discard the exception.
Don't let your program call Terminate () when you can avoid it. Terminate () is actually an exit () called good point. If terminate () is called, your program will die in an unpleasant way.
Just like unable to support unExpected (), Visual C does not fully support Terminate (). To verify in actual operation, run:
#include
#include
#include
Using namespace std;
void my_terminate_handler ()
{
Printf ("in my_terminate_handler / n");
Abort ();
}
int main ()
{
Set_terminate (my_terminate_handler);
Throw 1; // nobody catches this
Return 0;
}
According to the C standard, it is thrown an exception captured will result in call terminate () (this is one of the Subclause 15.5.1 mentioned earlier). Thus, the above program is output:
In my_terminate_handler
However, compile and run with Visual C , the program does not output anything.
1.3 Avoid Terminate
In our UNEXPECTED () example, Terminate () is finally called because f () throws Unexpected exceptions. Our UNEXPECTED_HANDLER () tries to block this unpleasant thing, through throwing a new exception, but it is not successful; this throwing behavior ends due to another problem that it is trying to solve. We need to find a method to make the UNEXPECTED () handle the function to control the other parts of the program (assuming that the part of the program is smart enough, it can successfully drop an exception) without causing the program to terminate.
Very happy, the C standards provide such a method. As we have seen, the abnormal object thrown from the UNEXPECTED () handle must meet (old abnormal violation) abnormal specifications. This rule has an exception: if the contained exception specifications include type Bad_exception, a Bad_Exception object will replace the object thrusted by the UNEXPECTED () process. E.g:
#include
#include
Using namespace std;
void my_unexpected_handler ()
{
Throw 1;
}
Void f () throw (char, bad_exception) {
Throw 1L; // OOPS - * BAD * FUNCTION
}
int main ()
{
Set_unexpected (my_unexpected_handler);
Try
{
f ();
}
Catch (Bad_Exception Const &)
{
Printf ("CAUGHT BAD_EXCEPTION / N");
// ... Even Though Such An Exception Was Never Thrown
}
Return 0;
}
Compile and run with C standard compatible compiler, program output:
CAUGHT BAD_EXCEPTION
When compiling and running with Visual C , the program does not output anything. Because Visual C did not capture Unexpected exceptions in the first throwing place, it didn't have the opportunity to replace Bad_Exception.
The same is the same as the previous example, f () still violates its abnormal specifications, and my_unexpected_handler () still throws an int. The difference is: F () exception specifications declared containing Bad_Exception. As a result, the program quietly replaces my_unexpected_handler () the original INT object to the BAD_EXCEPTION object. Because the Bad_Exception exception is allowed, TERMINATE () is not called, and this Bad_Exception exception can be captured by other parts of the program.
Final Result: The last line exception thrown from f () first is mapped to int, and is mapped to Bad_Exception. Such mapping not only avoids the re-abnormal problem of Terminate, but also a correction opportunity to other parts of the program. The presence of the Bad_Exception anomaly object indicates that some unExpected exception is initially thrown. The program can be resumed by capturing such an object near the problem point.
I also noticed a strange place. In the code, you see f () throw a long, my_unexpected_handler () throws an int, and no one throws Bad_EXCEPTION, but main () does capture a bad_exception. Yes, the program captures an object that it has never thrown. As I know, the only place that is allowed to happen is the interaction between UNEXPECTED anomaly handling functions and Bad_exception.
1.4 A more special function
The C standard defines three "special" functions to capture exceptions. Among them, you have seen terminate () and undexpected (). Finally, the simplest is uncaGHT_EXCEPTION (). From the C standard (15.5.3):
Function BOOL Uncaught_Exception () Returns True between the exceptions of the exception to the matching exception handler is completed. Includes the decorph of it. If the exception is thrown again, uncaught_exception () is returned to TRUE between the rebandef.
Uncaunht_exception () Let you see if the program throws an exception and has not been captured. This function has a special meaning of the destructor:
#include
#include
Using namespace std;
Class X
{
PUBLIC:
~ X ();
}
X :: ~ x ()
{
IF (uncaunht_exception ())
Printf ("x :: ~ x called during stack unwind / n"); ELSE
Printf ("x :: ~ x called normally / n");
}
int main ()
{
X x1;
Try
{
X x2;
Throw 1;
}
Catch (...)
{
}
Return 0;
}
Program output in a C standard compatible environment:
X :: ~ x Called During Stack Unwind
X :: ~ x called normally
X1 and x2 throw an abnormal pre-abnormality in main (). Call the destructor of the X2 when the stack is returned. Because an unprepired exception is active during the destructor call, uncaunht_exception () returns True. Then, the destructor of X1 is called (when main () exits), the exception has been restored, and uncaunht_exception () returns false.
As before, Visual C does not support C standards here. Compile, program output:
X :: ~ x called normally
X :: ~ x called normally
If you know the SEH of Microsoft (I have been talked in the second part), you know Uncaught_Exception () similar to the ABNORMALTERMINATION () of SEH. Within their respective applications, both functions are detected whether an abnormality that is thrown is still inactive and still being captured.
1.5 small knot
Most functions are not directly throwing, but the abnormalities throwing out of other functions. It is very difficult to decide which abnormality is transmitted, especially from a function that has no abnormal specifications declared. BAD_EXCEPTION () is a secure valve that provides a method to protect those you can't fully resolve.
These protective energy work, but, like a normal exception handler, you need to design it clearly. You must remember to add a bad_exception in its abnormal specifications and capture it at someone. Bad_exception and other exceptions have no difference: if you don't want to capture it, don't generate it. A Bad_Exception that is not captured will cause the program to terminate, just like the original place, you do not use Bad_Exception to replace it.
Abnormal specifications Statement make you clear. It said, "This is the collection of anomalies I allow this function to thrown; if the function throws another thing, it is not my design is wrong is the program is buggy." A UNEXPECTED is abnormal, regardless of how it appears, indicates a logical error. I suggest you better happen in a foreseeable way.
All of this indicates that you can depict your code at the beginning of the start. Unfortunately, such depiction is close to witchcraft. Next time, I will give some guidelines to analyze the exceptions in your code.