< C++ .NET Exception Handling 4 | Main | C++ .NET Exception Handling 6 >


 

 

Exception Handling 5

 

 

What we have in this page?

 

  1. Using __try_cast (old syntax, Managed Extension for C++) for Dynamic Casting

  2. safe_cast for Dynamic Casting (new syntax)

  3. Upcast with safe_cast

  4. Downcast with safe_cast

 

 

Using __try_cast (old syntax, Managed Extension for C++) for Dynamic Casting

 

For a complete Typecasting in C++, please read C and C++ Typecasting. C++ supports the idea of casting, wherein you tell the compiler to convert one type into another to use it in an expression. Although casting can be useful, it can also be dangerous, and the __try_cast keyword has been introduced in the Managed Extensions for C++ to help make the operation safer. The following code fragment shows both safe and unsafe casting:

// Define the Vehicle and Car classes

// Compile with /clr:OldSyntax

__gc class Vehicle {};

__gc class Car : public Vehicle {};

__gc class Truck : public Vehicle {};

__gc class Bus : public Vehicle {};

// ...

// Create a Car

Car* pc = new Car();

// Point to it using a Vehicle pointer - OK

Vehicle* pv = pc;      

// ...

// Copy pv into another Car* pointer - not OK!

Car* pc2 = pv;

The compiler raises an error on the last line, complaining that it can’t convert a Vehicle* to a Car*. The problem is that a Vehicle pointer could point to any object derived from Vehicle, such as a Truck or a Bus. Implicitly casting from a Car to a Vehicle is fine because a Car is a Vehicle; going the other way doesn’t work because not every Vehicle is a Car. The way around this issue is to use the __try_cast construct, like this:

try

{

    Car* pc2 = __try_cast<Car*>(pv);

}

catch(System::InvalidCastException* pce)

{

    Console::WriteLine("Cast failed");

}

At run time, __try_cast checks the object on the other end of the pointer to see if it has the same type as the object you’re trying to cast to. If it does, the cast works; if it doesn’t, an InvalidCastException is thrown. dynamic_cast construct supported by standard C++. The difference is that __try_cast throws an exception if the cast fails.

 

safe_cast for Dynamic Casting (new syntax)

 

safe_cast allows you to change the type of an expression and generate verifiable MSIL code. The syntax is shown below.

 

Syntax

[cli]::safe_cast<type-id>(expression)

 

 

Parameters

Description

type-id

A handle to a reference or value type, a value type, or a tracking reference to a reference or value type.

source

An expression that evaluates to a handle to a reference or value type, a value type, or a tracking reference to a reference or value type.

 

Table 3

 

The expression safe_cast<type-id>(expression) converts the operand expression to an object of type type-id. The compiler will accept a static_cast in most places that it will accept a safe_cast. However, safe_cast is guaranteed to produce verifiable MSIL, where as a static_cast could produce unverifiable MSIL. static_cast, safe_cast invokes user-defined conversions. safe_cast is a keyword defined inside the cli namespace, which is a compiler-defined namespace. safe_cast does not apply a const_cast (cast away const). safe_cast is in the cli namespace. One example of where the compiler will not accept a static_cast but will accept a safe_cast is for casts between unrelated interface types. With safe_cast, the compiler will not issue a conversion error and will perform a check at runtime to see if the cast is possible.

 

1.        Create a new CLR Console Application project named mysafecast. Try the following code.

 

Exception handling -Creating a new CLR Console Application project named mysafecast

 

// mysafecast.cpp : main project file.

// compile with: /clr

#include "stdafx.h"

 

using namespace System;

 

interface class I1

{

      // more codes...

};

interface class I2

{

      // more codes

};

interface class I3

{

      // more codes

};

 

ref class X : public I1, public I2

{

      // more codes

};

 

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

{

   I1^ i1 = gcnew X;

   // OK, I1 and I2 have common type: X

   // I2^ i3 = static_cast<I2^>(i1);   C2440 use safe_cast instead

   I2^ i2 = safe_cast<I2^>(i1);

   try

   {

         // fail at runtime, no common type

         I3^ i4 = safe_cast<I3^>(i1);

   }

   catch(InvalidCastException^)

   {

         Console::WriteLine("Caught expected exception");

   }

 

    return 0;

}

 

Output:

Exception handling - the safe_cast console output program sample

 

Upcast with safe_cast

 

An upcast is a cast from a derived type to one of its base classes. This cast is safe and does not require an explicit cast notation. The following sample shows how to perform an upcast with and without safe_cast. You may use the previous project for this example.

// mysafecast.cpp : main project file – safe_cast and upcast.

// compile with: /clr

#include "stdafx.h"

using namespace System;

 

interface class myA

{

   void Test();

};

 

ref struct myB : public myA

{

      virtual void Test()

      {

            Console::WriteLine("In myB::Test");

      }

 

      void Test2()

      {

            Console::WriteLine("In myB::Test2");

      }

};

 

ref struct myC : public myB

{

      virtual void Test() override

      {

            Console::WriteLine("In C::Test");

   };

};

 

interface class myI {/* more code...*/} ;

value struct myV : public myI {/* more code...*/};

 

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

{

      myC ^ cobj = gcnew myC;

      // implicit upcast, myC to myB

      myB ^ bobj = cobj;

      bobj->Test();

      bobj->Test2();

      // upcast with safe_cast, cobj type to myB

      bobj = nullptr;

      bobj = safe_cast<myB^>(cobj);

      myV ^ pv;

      // implicit upcast, myV to myI

      myI^ iobj = pv;

      // upcast with safe_cast, pv type to myI

      iobj = safe_cast<myI^>(pv);

      return 0;

}

 

Output:

 

 

 

 

 

 

 

---------------------------------------------

Exception handling - the upcast of the safe_cast console program output

 

Downcast with safe_cast

 

A downcast is a cast from a base class to a class derived from the base class. A downcast is only safe if the object addressed at runtime is actually addressing a derived class object. Unlike static_cast, safe_cast performs a dynamic check and throws InvalidCastException if the conversion fails. Use the previous project to try the following example.

// mysafecast.cpp : main project file – safe_cast and downcast.

// compile with: /clr

#include "stdafx.h"

using namespace System;

 

interface class myA { void Test(); };

 

ref struct myB : public myA

{

      virtual void Test() {

            Console::WriteLine("In myB::Test()");

      }

 

      void Test2() {

            Console::WriteLine("In myB::Test2()");

      }

};

 

ref struct myC : public myB

{

      virtual void Test() override {

        Console::WriteLine("In myC::Test()");

       }

};

 

interface class myI {/* more code */};

 

value struct myV : public myI {};

 

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

{

      myA^ aobj = gcnew myC();

      aobj->Test();

      myB^ bobj = safe_cast<myB^>(aobj);

      bobj->Test();

      bobj->Test2();

      myV vobj;

      myI^ iobj = vobj;   // iobj boxes myV

      myV^ refv = safe_cast<myV^>(iobj);

      Object^ oobj = gcnew myB;

      myA^ aobj2= safe_cast<myA^>(oobj);

      return 0;

}

 

Output:

Eception handling - the down cast of the safe_cast console program output sample

 

 

 

 

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

 


 

< C++ .NET Exception Handling 4 | Main | C++ .NET Exception Handling 6 >