Thursday, May 29, 2008

C++ puzzles #7

Executing a Member Function Before main()


We can execute any member function or any other execution before main() function by using:

 
       "#pragma startup".
 
Syntax:
      #pragma startup <function_name /*with no   
parenthesis*/> [priority]
//without semicolon


Example:

 
#include<iostream.h>
class CL{
public:
               //A constructor
               CL(){       cout <> "\nThe object is creating"; }
               //A member function
               void obj_func() { cout <> "\nThis is a function 
of object"; }
};
 
void func_before_main ();    //function declaration
 
#pragma startup func_before_main 65              //setting a function 
to execute before
main()
void main ()
{  cout <&;t; "\nThis is main() ";
}
// A function defination
void func_before_main() {
               cout << "\nThis is func_before_main() ";
               CL obj1;  //creating an object, and calling 
its constructor
               obj1.obj_func();      //calling a member function 
of an object
}


In defining such functions, the functions should not carry any attribute nor return any value. It should be defined before #pragma statement.

Priority is optional. It is ranging from 64 to 100 (recomended).

0-------Highest priority used by C Libraries

63------Lowest priority used by C Libraries

64------First available Priority

100-----Default priority

255-----Lowest priority.

Exceptions and operator new

Standard C++ states that operator new should throw an exception of type std::bad_alloc when it fails. This is different from previous stages of C++ and it means two important things: 1. If your code does not contain a catch statement to handle an std::bad_alloc exception, your program will crash whenever new fails, even if that exception occurs deep down in some library routine you never knew was there. 2. Testing whether the pointer returned from new == 0 is completely useless; in case of success, your test will waste system resources in vain, checking a condition which is already guaranteed to be false. (Because no exception has been thrown, the tested pointer cannot be a null pointer.). And in cases of failure, the thrown exception will abort the current thread of execution right from where it was thrown, so the next lines of code will not be executed:

 void* f(int size) {
 
               char *p = new char [size];
 
               //A COMPLETELY USELESS TEST
               If (p == 0) { //we won’t reach here if new failed. On the other           
        //hand, if we are here, it’s just a waste of time: 
        //new has succeeded.
                                               //...         
                               }
               
               return p;
}//end f()

The revised code should look like this:

 void main() {
 
               try {
 
                               char * p = f(65536);  //f() might throw an exception                                  //...rest of the code
 
 
               }
               catch(std::bad_alloc& ex)  //we’ll get right here whenever f()                                                                           //throws an exception
               {
                               cout<<“memory allocation failure”<
 

How to terminate a program safely?

In standard C, the functions abort() and exit() perform an immediate program termination, regardless of where they are called from. Although this also works in C++, it is usually not a good idea to terminate a program this way, even when a severe error has occurred. The problem with these functions is that none of them ensures that local objects' destructors are called: abort() calls no destructors at all and exit() invokes only the destructors of objects in its scope (but not in its caller). You can imagine the disastrous results when destructors of objects holding open files handles, dynamically allocated memory, mutexes, etc. are not called. A better choice is to throw an exception. The C++ Standard guarantees that when an exception is thrown, all local objects' destructors are called. If no handle for that exception is found, the program will terminate but only after all local objects in the entire calling chain have been invoked. Mind also that exceptions can give the handler a chance to fix the problem and continue, whereas abort() and exit() are irreversible.

Exception handlers hierarchy

It's handy to catch potential exceptions in a bottom-down hierarchy: specific exceptions are handled first, then groups of exceptions and finally, a catch-all handler:

 
#include  //std::except is the base class for  most standard exceptions
#include 
 
void main()
{
//...program body
 
catch(std::bad_alloc& alloc_failure) { //handle exceptions thrown by operator new 
  cout<<"memory allocation failure";
  //...other diagnostics and remedies
}
 
 
catch(std::except& std_ex) {//other exceptions derived from std::except caught here  
               cout<< std_ex.what() << main() end } exception?<<

Re-Throwing an Exception

An exception is thrown to indicate an abnormal state. The first handle to catch the exception can try to fix the problem. Should it fail to do that, or in case it only managed to perform a partial recovery, it can still (re-) throw the exception as long as there is a higher try-block, which has an appropriate catch-statement. In other words, try-blocks can be nested in a hierarchical order, enabling a re-thrown exception from a lower try-block's catch-statement to be caught. A re-throw is indicated by a throw without an operand:

 
class Exception {/*..*/}; //general base class for exceptions
class FileException: public Exception {/*..*/};
void main() {
File f ("db.dat");
try {
try {//inner try
                                              if (File.IsValid() == false )
                                              throw FileException("db.dat"); //construct FileException object and throw it
}//inner try
catch(FileException &fe)  { //first handler to cope with the exception
cout<<"invalid file specification" <<FE.ERROR()<<" a code < main } catch ); failed? also file new open to WriteToLog(?attempt exception re-thrown the will handler this ex){ & catch(Exception try outer it with deal higher let and original re-throw throw; !="SUCCESS)" (File.OpenNew() if file?; create trying>

Exceptions are Always Passed by Value

The exception handling (EH) mechanism disables the possibility of passing a thrown exception by reference or through a pointer. This is due to the way this mechanism is implemented: when an exception is thrown, the mechanism first searches for an appropriate handler in the current scope. If such handler does not exist, all local objects are destroyed, the exception object itself is copied on the stack of the function that is higher in the calling chain, and only then is the current scope exited. This process is very similar to a sequence of return statements, each returning the same object by value to its caller. The "stack unwinding" process is iterative and continues until an appropriate handler has been found or else the program terminates. Why is it still useful to declare a handler as if it caught an exception by reference? Simply, in order to allow a handler to catch any exception derived from the specified type:

 
class ExBase {/*_*/};
class FileEx: public ExBase {/*_*/};
 
void Write(FILE *pf) {
if (pf == NULL) throw FileEx(); 
}
void main ()
{
try { Write(NULL); //will cause a FileEx exception to be thrown 
}
catch(ExBase& exception) {//catch ExBase or any object derived from it--by value
//diagnostics and remedies
}
}

Of course, copying objects repeatedly is a costly process but it occurs only when an exception is thrown--something that should happen in abnormal and rare conditions, during which performance is negligible, compared to application's integrity and correctness.

Standard Exceptions

C++ defines a hierarchy of standard exceptions that are thrown at run time when abnormal conditions arise, such as when operator new fails. The standard exception classes are derived from std::exception class (defined in the <stdexcept> header). This hierarchy enables you to catch these exceptions in a single catch-statement:

 
catch (std::exception& exc) {
//handle exception of type std::exception as well as any exception derived from it 
}

The standard exceptions thrown by built-in operators of the language are:

 
bad_alloc               //may be thrown by operator new
bad_cast                //may be thrown by operator dynamic_cast < >
bad_typeid             //may be thrown by operator typeid
bad_exception       //may be thrown when an exception specification of a function is violated
 
Your code should contain appropriate handler(s) for these exceptions whenever you use any of these built-in operators.

 

Please note:

 
·         All standard exception classes are grouped under namespace std.
·          
·         The standard library has an additional set of exceptions thrown by its components.
·          

Exception Specifications are Checked at Run Time

A function can specify explicitly what type of exception it may throw. An exception specification, however, is not checked at compile time, but rather at run time:

 
class X {};
int f();        // no exception specification, can throw any type of exception
void g(int j) throw(); // promises not to throw any exception at all
{              
  int result = f(); // if f throws an exception, g will violate its  guarantee not to throw an exception
                               //still, this is a perfectly legal code
}

There are several reasons for this runtime checking policy. In the example, f could be a legacy C function. It is impossible to force a C function to have an exception specification. Also, forcing the programmer to write unnecessary try/catch blocks in g (although f doesn't throw any exception at all) is an unacceptable burden. For these reasons, an exception specification is checked at run time.

Do Not Use Operator New in a Throw Statement

Dynamic allocation of an exception (as in this example) is not a good idea:

 
class Err{
public: 
  Err(const char * description);
};
 
void f()
{
  if (disaster)
    throw new Err("failed");  //exception object is dynamically allocated on the free store
  //…
}

There are at least two problems with this approach. First, an exception may be thrown due to memory exhaustion. In this case, trying to allocate more memory from the free store will surely fail (with disastrous results). The second problem is more common; the memory allocated by operator new never releases. Consequently, memory leaks will occur. Creating a temporary exception object is the preferred method:

 
if (disaster)
    throw Err("failed"); //temporary object

It is guaranteed that the temporary object persists until the appropriate handler that handles the exception has completed. By creating a temporary exception object, you are avoiding the risk of allocation failure and your programs do not incur memory leaks.

Catch Exceptions by Reference

Although you can catch an exception by value, as in the following example:

 
  catch( Exception except)  // by value
  {
    //…
  }

It's better to catch an exception by reference:

 
  catch( Exception& except)  // by reference
  {
    //…
  }

There are several advantages in catching an exception by reference. First, you prevent unnecessary copying of large exception object. Second, you ensure that modifications applied to the exception within a catch-clause are preserved in the exception object when it is re-thrown. Third, you avoid slicing of derived exception objects.

Checking for an Uncaught Exception

A thrown exception is considered caught, when its corresponding handler has been entered (or, in case such a handler cannot be found, when unexpected() function has been invoked). Sometimes you have to check whether an uncaught exception is currently being processed. Consider a destructor that may throw an exception. If the destructor has been invoked due to stack unwinding caused by another exception, throwing an additional exception from this destructor will yield an undefined behavior. To check whether a thrown exception is currently being processed, you can use the standard function uncaught_exception():

 
class FileException{};
File::~File() throw (FileException);
{
   if ( close(file_handle) != success) // failed to close current file?
  {
    if (uncaught_exception()  == true ) // is there any uncaught exception being processed currently?
       return;  // if so, do not throw an exception
    throw FileException(); // otherwise, it is safe to throw an exception to signal an error
  }
return; // success
}

Add a catch(...) Statement to Your Exception Handlers' Hierarchy

If your application uses exception handling, it is recommended that you always add a catch(...) statement after all the existing catch statements. Remember that even if your code contains the appropriate handlers for expected exception, exceptions of unexpected types may be thrown by standard functions, algorithms and containers. By adding a catch(...) statement at the end of existing handlers you guarantee that no exception is left unhandled. Furthermore, a catch(...) statement ensures that the program properly destroys all local objects and closes open files and streams as part of the stack unwinding process in the event of an exception. For example:

         
int main()
{
  try
  {
    func();
  }
  catch(DerivedEx& de) //most derived exception
  {}
  catch(Ex& exc)  
  {}
  catch(...) //catch any other unexpected exception
  {}
}

Note that catch(...) cannot detect the type of the exception it handles. Therefore, you should perform only general cleanup and logging operations inside a catch(...) block.

 

No comments: