Showing posts with label type casting. Show all posts
Showing posts with label type casting. Show all posts

Sunday, June 13, 2010

const casting

The "const_cast" keyword.
  • Operates on references and pointers only.
  • When casting into a const the type is simply set as const.
  • When casting out of a const on a fundamental data type a copy of the variable is made on the FIRST local removal of the const only. This is to move the variable from the um-manipulable BSS/data segment into the current stack area.
  • When casting out the const on class the const status is simply removed and the object becomes directly modifiable.

//compile with g++
#include <iostream>
using namespace std;

class BaseA
{
public:
  int a;

  BaseA()         {a = 0;   cout << "BaseA create" << endl; }
  BaseA(BaseA& o) {a = o.a; cout << "BaseA copy" << endl; }
};

void print(int& a)
{ 
  cout << "print(int a) step1:" << a++ << endl; 
  cout << "print(int a) step2:" << a++ << endl; 
}

void print(int* a)
{ 
  cout << "print(int a) step1:" << (*a)++ << endl; 
  cout << "print(int a) step2:" << (*a)++ << endl; 
}

void print(const int& a)
{ cout << "print(const int a):" << a << endl; }

void print(BaseA& a)
{ 
  cout << "print(BaseA& a) step1:" << a.a++ << endl; 
  cout << "print(BaseA& a) step2:" << a.a++ << endl; 
}

void print(const BaseA& a) 
{ cout << "print(const BaseA& a):" << a.a << endl; }


int main()
{
  int       int1 = 100;
  const int int2 = 200;
  
  //print(const_cast<const int>(int1)); //BAD only works on pointers and references
  //print(const_cast<int>(int2));       //BAD only works on pointers and references

  cout << "const references" << endl;
  const int& refInt1 = const_cast<const int&>(int1); 
  int&       refInt2 = const_cast<int&>(int2);  //OK but get the value from the BSS and load it into the stack   

  refInt2++;
  print(refInt1); 
  print(refInt2); 

  cout << "Original: int1 = 100; is NOW: "  << int1 << endl;
  cout << "Original: int2 = 200; is NOW: "  << int2 << endl;

  cout << "Cast: refInt1; is NOW: "  << refInt1 << endl;
  cout << "Cast: refInt2; is NOW: "  << refInt2 << endl;
  cout << endl;

  cout << "const pointers" << endl;
  int*       ptrInt2 = const_cast<int*>(&int2);  //OK but get the item was already loaded in to the stack
  cout << "Cast: ptrInt2; is NOW: "  << *ptrInt2 << endl;

  print      (ptrInt2); 

  cout << "Original: int2 = 200; is NOW: "  << int2 << endl;
  cout << "Cast: ptrInt2; is NOW: "  << *ptrInt2 << endl;

  cout << endl;

  cout << "const classes" << endl;
  BaseA       a1;
  const BaseA a2;

  const BaseA& refA1 = const_cast<const BaseA&>(a1);
  BaseA&       refA2 = const_cast<BaseA&>(a2);  

  refA2.a++;
  print(refA1); 
  print(refA2); 

  cout << "Original: a1.a = 0; is NOW: "  << a1.a << endl;
  cout << "Original: a2.a = 0; is NOW: "  << a2.a << endl;

  cout << "Cast: refA1.a; is NOW: "  << refA1.a << endl;
  cout << "Cast: refA2.a; is NOW: "  << refA2.a << endl;
}

reinterpret casting

The "reinterpret_cast" keyword.
  • Operates on references and pointers.
  • It results in only the direct coping of the pointer/reference and nothing more. This means that the exact value being pointed to is unchanged and will be interpreted as the new type directly.
  • The exact way the contents of the pointer are interpreted after conversion is very system dependent.
  • This cast is normally used to convert c++ data structures into something usable by a 3rd party API or lib, or in buffer pools and variable data type implementations. If a chunk of data has multiple interpretations to (like IP headers etc) then a "union" is better choice.

//compile with g++
#include <iostream>
using namespace std;

class BaseA
{
  virtual void print() { cout << "BaseA"; }
};

class DerivedA1 : public BaseA
{
  int a;
  virtual void print() { cout << "DerivedA1"; }
};

class DerivedA2 : public BaseA
{
  virtual void print() { cout << "DerivedA2"; }
};

class DerivedA3 : public DerivedA1
{
  virtual void print() { cout << "DerivedA3"; }
};

class BaseB
{
public:
  BaseB()         { }
  BaseB(BaseA& A) { cout << "Converting BaseA" << endl; }
  virtual void print() { cout << "BaseA"; }
};

int main()
{
  int    int1         = 100;
  double double1      = 3.142;

  cout << "reinterpret casting: non-conversion of data example " << endl;
  char*      char1    = reinterpret_cast<char*>(&int1);  //OK keep in mind the endianess of the system
  int*       int2     = reinterpret_cast<int*>(&double1); //OK be aware of the proceses native double formats

  cout << "reinterpret_cast<char>(int1)  " << *char1 << " <- " << int1    << endl;
  cout << "reinterpret_cast<int>(double) " << *int2  << " <- " << double1 << endl;
  cout << endl;

  BaseA*     base1    = new BaseA();
  BaseB*     baseB    = new BaseB();
  BaseA*     base2    = new DerivedA2();
  BaseA*     base3    = new DerivedA3();
  DerivedA3* derived1 = new DerivedA3();

  cout << "reinterpret casting: pointers " << endl;
  DerivedA1* derived2 = reinterpret_cast<DerivedA1*>(base1);  //BAD base1 doesnt have the correct memory to be DerivedA1 class!   
  DerivedA1* derived3 = reinterpret_cast<DerivedA1*>(base2);
  DerivedA2* derived4 = reinterpret_cast<DerivedA2*>(base2);
  DerivedA1* derived5 = reinterpret_cast<DerivedA1*>(base3);
  BaseA*     base4    = reinterpret_cast<BaseA*>(derived1);   //OK but overkill

  char*      charPtr  = reinterpret_cast<char*>(&int1);     //OK but be careful int and char dont share any inhertance.
  BaseB*     base5    = reinterpret_cast<BaseB*>(derived1); //BAD BaseB and dervied1 have conflicting memory layouts 

  cout << "reinterpret casting: references " << endl;
  DerivedA1& refDerived1    = reinterpret_cast<DerivedA1&>(*base1); //BAD base1 doesnt have the correct memory to be DerivedA1 class! 
  DerivedA1& refDerived2 = reinterpret_cast<DerivedA1&>(*base2);
  DerivedA2& refDerived3 = reinterpret_cast<DerivedA2&>(*base2);
  DerivedA1& refDerived4 = reinterpret_cast<DerivedA1&>(*base3);
  BaseA& refBase         = reinterpret_cast<BaseA&>(*derived1);
  BaseB& refBaseB        = reinterpret_cast<BaseB&>(*derived1); //BAD BaseB and dervied1 have conflicting memory layouts 

  delete base1;
  delete baseB;
  delete base2;
  delete base3;
  delete derived1;
}

static typecasting in C++

The "static_cast" keyword.
  • Operates on references and pointers of class and on the inbuilt data types and class with conversion constructors.
  • In the case of inbuilt data types it converts the value into the new type.
  • In the case of conversion constructor classes, it creates a new object from the original object using the conversion constructor. Note that copy constructors ARE conversion constructors and WILL be implicitly apply to any derived class.
  • In the case of pointers and references:

    • The cast confirms at compile time that the pointers convert from and to are are related by inheritance, ie they share the same Base class a some point.
    • Since the cast performs no RTTI check the it is faster and lighter than the "dynamic_cast".
    • If used badly on pointers or references it can convert invalidly, for example a base class can be converted to a Derived class despite the fact that it wont have memory for the derived classes member variables.

//compile with g++
#include <iostream>
using namespace std;

class BaseA
{
  virtual void print() { cout << "BaseA"; }
};

class DerivedA1 : public BaseA
{
  int a;
  virtual void print() { cout << "DerivedA1"; }
};

class DerivedA2 : public BaseA
{
  virtual void print() { cout << "DerivedA2"; }
};

class DerivedA3 : public DerivedA1
{
  virtual void print() { cout << "DerivedA3"; }
};

class BaseB
{
public:
  BaseB()         { }
  BaseB(BaseA& A) { cout << "Converting BaseA" << endl; }
  virtual void print() { cout << "BaseA"; }
};

int main()
{
  int    int1         = 100;
  double double1      = 3.142;

  cout << "static casting: inbuilt datatypes " << endl;
  char       char1    = static_cast<char>(int1); 
  int        int2     = static_cast<int>(double1); 

  BaseA*     base1    = new BaseA();
  BaseB*     baseB    = new BaseB();
  BaseA*     base2    = new DerivedA2();
  BaseA*     base3    = new DerivedA3();
  DerivedA3* derived1 = new DerivedA3();

  cout << "static_cast<char>(int1)  " << char1 << " <- " << int1    << endl;
  cout << "static_cast<int>(double) " << int2  << " <- " << double1 << endl;
  cout << endl;

  cout << "static casting: conversion constructor classes " << endl;
  BaseB      objB     = static_cast<BaseB>(*base1);
  BaseA      objA1    = static_cast<BaseA>(*base2); //OK the default copy constructor IS a conversion constructor 
  //BaseA      objA2    = static_cast<BaseA>(*baseB); //BAD no conversion constructor 

  cout << "static casting: pointers " << endl;
  DerivedA1* derived2 = static_cast<DerivedA1*>(base1);  //BAD base1 doesnt have the correct memory to be DerivedA1 class!   
  DerivedA1* derived3 = static_cast<DerivedA1*>(base2);
  DerivedA2* derived4 = static_cast<DerivedA2*>(base2);
  DerivedA1* derived5 = static_cast<DerivedA1*>(base3);
  BaseA*     base4    = static_cast<BaseA*>(derived1);   //OK but overkill

  //char*      charPtr  = static_cast<char*>(&int1);     //BAD int and char dont share any inhertance.
  //BaseB*     base5    = static_cast<BaseB*>(derived1); //BAD BaseB and dervied1 dont share any inhertance.

  cout << "static casting: references " << endl;
  DerivedA1& refDerived1    = static_cast<DerivedA1&>(*base1); //BAD base1 doesnt have the correct memory to be DerivedA1 class! 
  DerivedA1& refDerived2 = static_cast<DerivedA1&>(*base2);
  DerivedA2& refDerived3 = static_cast<DerivedA2&>(*base2);
  DerivedA1& refDerived4 = static_cast<DerivedA1&>(*base3);
  BaseA& refBase         = static_cast<BaseA&>(*derived1);
  //BaseB& refBaseB        = static_cast<BaseB&>(*derived1); //BAD BaseB and dervied1 dont share any inhertance.

  delete base1;
  delete baseB;
  delete base2;
  delete base3;
  delete derived1;
}

Saturday, June 12, 2010

dynamic typecasting in C++

The "dynamic_cast" keyword.
  • Operates on references and pointers only.
  • Requires the item be a class and that class to be a polymorphic class, ie it has at least one virtual function
  • Uses the RTTI information to check the capability of the conversion at runtime
  • In the case of references it will throw an exception if the conversion failed.
  • In the case of pointers it will return NULL if the conversion failed.

//compile with g++
#include <iostream>
using namespace std;

class BaseA 
{
  virtual void print() { cout << "BaseA"; }
};

class DerivedA1 : public BaseA 
{
  virtual void print() { cout << "DerivedA1"; }
};

class DerivedA2 : public BaseA 
{
  virtual void print() { cout << "DerivedA2"; }
};

class DerivedA3 : public DerivedA1
{
  virtual void print() { cout << "DerivedA3"; }
};

int main()
{
  int int1 = 100;
  BaseA*     base1 = new BaseA();

  //derived class dont need casting into the base
  BaseA*     base2    = new DerivedA2();
  BaseA*     base3    = new DerivedA3();
  DerivedA3* derived1 = new DerivedA3();

  cout << "dynamic casting: pointers " << endl;
  //char*      char1    = dynamic_cast<char*>(&int1); //BAD requires a class
  DerivedA1* derived2 = dynamic_cast<DerivedA1*>(base2);
  DerivedA2* derived3 = dynamic_cast<DerivedA2*>(base2);
  DerivedA1* derived4 = dynamic_cast<DerivedA1*>(base3);
  BaseA*     base4    = dynamic_cast<BaseA*>(derived1); //OK but overkill
  
  cout << "dynamic_cast<DerivedA1*>(base2); " << (derived2 ? "PASSED" : "FAILED") << endl;
  cout << "dynamic_cast<DerivedA2*>(base2); " << (derived3 ? "PASSED" : "FAILED") << endl;
  cout << "dynamic_cast<DerivedA1*>(base3); " << (derived4 ? "PASSED" : "FAILED") << endl;
  cout << "dynamic_cast<BaseA*>(derived1);  " << (base4 ? "PASSED" : "FAILED") << endl;
  cout << endl;

  cout << "dynamic casting: references " << endl;
  cout << "dynamic_cast<DerivedA1&>(*base2) ";
  try
    { 
      DerivedA1& refDerived = dynamic_cast<DerivedA1&>(*base2); 
      cout << "PASSED" << endl; 
    }
  catch (...)
    { cout << "FAILED" << endl;  }
    
  cout << "dynamic_cast<DerivedA2&>(*base2) ";
  try
    {
      DerivedA2& ptrDerived = dynamic_cast<DerivedA2&>(*base2); 
      cout << "PASSED" << endl;
    }
  catch (...)
    { cout << "FAILED" << endl;  }

  cout << "dynamic_cast<DerivedA1&>(*base3) ";
  try
    { 
      DerivedA1& refDerived = dynamic_cast<DerivedA1&>(*base3); 
      cout << "PASSED" << endl; 
    }
  catch (...)
    { cout << "FAILED" << endl;  }

  cout << "dynamic_cast<BaseA&>(*derived1)  ";
  try
    { 
      BaseA& refBase = dynamic_cast<BaseA&>(*derived1); 
      cout << "PASSED" << endl; 
    }
  catch (...)
    { cout << "FAILED" << endl;  }

  delete base1;
  delete base2;
  delete base3;
  delete derived1;
}