< C++ .NET Exception Handling 3 | Main | C++ .NET Exception Handling 5 >


 

 

Exception Handling 4

 

 

  1. The catch (...) Block

  2. Creating Your Own Exception Types

  3. Using value Classes

 

 

The catch (...) Block

 

C++ has a construct that is sometimes seen in traditional code and is used to catch any exception that goes past. Here’s how it works:

try

{

    // do some arithmetic operation

}

catch(System::ArithmeticException^ pex)

{

    // handle this exception

}

// catch with three dots...

catch(...)

{

    // handle any exception

}

If an exception doesn’t match the first catch block, it will get caught by the second one no matter what type it is. The problem is that you lose any information about the exception, because the catch(...) block doesn’t have an argument. If you want this functionality when using managed exceptions, use a catch block that has an Exception^ as its argument, which will catch any managed exception object.

 

Creating Your Own Exception Types

 

You’ve already seen how all the exception types are derived from the System::Exception class. If you can’t find one that suits your needs in the standard exception hierarchy, you can easily derive your own class from Exception and use it in your code. The following exercise shows you how to derive a new exception class and how to use it in code.

 

1.        Start Visual C++/Studio .NET, and create a new CLR Console Application project named OwnExcept.

 

 

Creating a new CLR Console Application project named OwnExcept

 

2.        Add the following class definition immediately after the using namespace System; line:

// User-defined exception class

ref class MyException : public System::Exception

{

   public:

       int errNo;

       MyException(String^ msg, int num) : Exception(msg), errNo(num) {}

};

 

Exception handling - adding user defined exception class

 

This custom exception class is a managed class that inherits from System::Exception, and it extends Exception by adding a single field to hold an error number. The class constructor takes a message and a number, and passes the message string back to the base class. We’ve made the errNo field public. Although you’re normally advised to make all data members of classes private, you can make a case for having public data members in certain circumstances. Once you’ve created an Exception object and passed it back to the client, do you care what the client does with it? Exceptions are “fire and forget” objects, and you’re normally not concerned with the integrity of their state once they leave your code in a throw statement.

 

3.        Add the following function definition immediately after the class definition:

void func(int a)

{

    try

    {

       if (a <= 0)

         throw gcnew System::ArgumentException(L"Negative argument");

    }

    catch(System::ArgumentException^ pex)

    {

       Console::WriteLine(L"Caught ArgumentException"

                L"in func()");

       throw gcnew MyException(pex->Message, 1000);

    }

}

Exception handling - Adding a function definition for the try-catch-throw class  

 

The function checks its argument and throws a System::ArgumentException if it finds a negative value. This exception is caught locally, and a message is printed. Now we decide that we really want to handle the exception elsewhere, so we create a new MyException object and re-throw it, initializing it with the message from the original ArgumentException.

 

4.        Test the exception handling by calling the function in the program’s main() routine.

int main(array<System::String ^> ^args)

{

    Console::WriteLine(L"Custom Exceptions");

    try

    {

        func(0);

    }

    catch(MyException^ pex)

    {

        Console::WriteLine(L"Caught MyException in main()");

        Console::WriteLine(L"Message is : {0}", pex->Message);

        Console::WriteLine(L"Error number is : {0}", (Object ^)(pex->errNo));

    }

    return 0;

}

Exception handling - Adding code in the main program to test the custom exception handling

 

5.        Build and run your program.

 

Calling the function with a 0 value triggers the exception, which is handled in the function itself, and the exception is then re-thrown to be handled in the main() function. You can see from the following figure how the exception has been caught in both in places.

 

Exception handling - testing the custom exception handling class

 

In the preceding code, notice that it’s necessary to box the error number before it can be used in a call to WriteLine because the formatted overloads to WriteLine need a list of Object^ pointers, and boxing lets you use a built-in type as an object.

 

Using value Classes

 

In the preceding example, you implemented the custom exception as a managed class (declared using the ref (__gc for old syntax) keyword), but it’s also possible to use value types (declared with the value (__value for old syntax) keyword) as custom exceptions. The difference comes when you want to use value types as exceptions because you need to throw a pointer to a managed type. Getting the pointer is not a problem with ref classes because you always create them using gcnew (new for old syntax) and you always get a pointer returned. Value types are created on the stack, and you operate on them directly, rather than through pointers. If you want to get a pointer to a value type, you have to box it. Boxing wraps, or boxes, the value type in an object and returns a pointer to it. Here’s an example of how you’d use a value type as an exception:

value struct MyExceptionStruct

{

    int errNo;

};

 

void SomeFunction()

{

    // I want to throw a struct...

    MyExceptionStruct mes = { 1000 };

    throw (Object^)(mes);

}

The call to the Object^ function wraps the struct in an object box and returns a pointer to the object, which can then be thrown.

 

 

 

 

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6 | Part 7

 


 

< C++ .NET Exception Handling 3 | Main | C++ .NET Exception Handling 5 >