telecom • networking • design

an EventHelix.com blog


Leave a comment

C++ Internals: Mapping Inheritance and Virtual Functions to C

C++ to C conversionWe have already looked at mapping from C++ classes and methods to C structures and functions in a previous blog post. Here we will map inheritance and virtual functions to C code.

This blog post is based on the article mapping from C++ inheritance and virtual functions to C code. The contents of the article are summarized below.

Mapping a base class with virtual functions to C

A C++ base class with virtual functions requires extra logic to support the machinery for handling virtual functions. The following C++ base class….

class Shape
{
protected:
  int m_x;
  int m_y; 

public:
  virtual void Draw() = 0; 
  virtual void MoveTo(int newX, int newY);
  void Erase();
  Shape(int x, int y);
  virtual ~Shape();
};

…maps to the following C code.

typedef void (*VirtualFunctionPointer)(...);

struct VTable
{
   int d;  /* d and i are used in multiple inheritance */
   int i;
   VirtualFunctionPointer pFunc;
};

VTable VTableArrayForShape[] =
{
    { 0, 0, (VirtualFunctionPointer) pure_virtual_called_error_handler },
    { 0, 0, (VirtualFunctionPointer) Shape_MoveTo },
    { 0, 0, (VirtualFunctionPointer) Shape_Destructor }
};
struct Shape
{
  int m_x;
  int m_y;
  VTable *pVTable;
};

Points to note here are:

  • VTable structure used by the compiler to keep track of the virtual functions associated with a class.There is one instance of a VTable for every class containing virtual functions. All instances of a given class point to the same VTable.
  • The C++ compiler inserts an extra pointer to a vtable which will keep a function pointer to the virtual function that should be called.
  • The VTable pointer in the base class points to an array of VTables. The number of entries in the array of VTables depends upon the number of virtual functions in the class.
  • Note that a pure virtual function maps to function that will result in an exception.

For details refer to the article: Mapping from C++ inheritance and virtual functions to C code

Mapping an inherited class with virtual functions to C

The inheriting Circle class….

class Circle : public Shape
{
private:
   int m_radius;    // Radius of the circle

public:
   // Override to draw a circle
   virtual void Draw();   

   // Constructor for Circle
   Circle(int x, int y, int radius);

   // Destructor for Circle
   virtual ~Circle();
};

…maps to a new VTable array and a new struct:

VTable VTableArrayForCircle[] =
{
    { 0, 0, (VirtualFunctionPointer) Circle_Draw },
    { 0, 0, (VirtualFunctionPointer) Shape_MoveTo },
    { 0, 0, (VirtualFunctionPointer) Circle_Destructor }
};

struct Circle
{
   /* Fields inherited from Shape */
   int m_x;
   int m_y;
   VTable *pVTable;

   /* Fields added by Circle */
   int m_radius;
};
  • The inheriting class preserves structure offsets for inherited base class fields and the VTable array pointers.
  • A new VTable array is defined for the inherited class.
    • The pure virtual function entry has now been replaced with a call to Circle’s Draw function.
    • The entry for a function that is not being inherited is preserved (Shape’s MoveTo)
    • Shape class has a virtual destructor. The destructor vtable entry now points to Circle’s destructor.

For details refer to the article: Mapping from C++ inheritance and virtual functions to C code

Mapping a virtual function call to C code

A virtual function call like…

 pShape->Draw();

…is replaced by a call through a virtual table function pointer.

  (pShape->pVTable[0].pFunc)(pShape);

Explore More

We have covered basics of inheritance handling here. For details about constructor and destructor definitions and vtable array pointer initialization refer to the article:  Mapping from C++ inheritance and virtual functions to C code.


2 Comments

C++ Internals: Mapping from C++ classes and methods to C structs and functions

Mapping from C++ to C code

C++ internals can be better understood by mapping them to corresponding C code.

Refer to the article mapping from C++ classes and methods to C structs and functions.

The contents of the article are summarized below.

This pointer maps to an additional parameter

A C++ method maps to a C method with an additional this pointer. For example, the UpdateX method C++ method…

void A::UpdateX(int newX)
{
  if (g_y != 0 && m_x < newX)
  {
    m_x = newX;
  }
}

…translates to the following C code:

void A_UpdateX(A *this_ptr, int newX)
{
  if (g_y != 0 && this_ptr->m_x < newX)
  {
     this_ptr->m_x = newX;
  }
}

Complete article: Mapping from C++ classes and methods to C structs and functions.

A class maps to a struct with associated methods

A C++ class is equivalent to a C structure with a class methods. The following class…

class A
{
private:
  int m_x;
  static int g_y;
  int m_z;

  // Should be invoked when the object ends
  void InformEnd();

public:
  A(int x);
  ~A();
  void UpdateX(int newX);
  static void UpdateY(int newY);
};

…is represented as the C structure with functions.

  • Methods have a corresponding this pointer.
  • Static methods are functions that do not get an additional this pointer.
  • Static variables map to global variables.
struct A
{
  int m_x;
  int m_z;
};
int g_y = 0; /* Represents static variable */
A *A_Constructor(A *this_ptr, int x);
void A_Destructor(A *this_ptr, BOOLEAN dynamic);
void A_UpdateX(A *this_ptr, int newX);
void A_UpdateY(int newY); /* Represents a static method. */

Complete article: Mapping from C++ classes and methods to C structs and functions.

Constructor representation in C

In C++, objects may be allocated on the heap or the stack. This has implications in the mapping from a C++ constructor to an initialization function in C.

A::A(int x)
{
  m_x = x;
  m_z = 0;
}

The mapped constructor takes an additional this pointer. A NULL is passed as the this pointer when the object has to be allocated on the heap. A valid stack address is passed when an object is on the stack.

If a NULL pointer is passed to the construction function, it allocates memory before performing the initialization.

A *A_Constructor(A *this_ptr, int x)
{
  /*Check if memory has been allocated for struct A. */
  if (this_ptr == NULL)
  {
    /*Allocate memory of size A. */
    this_ptr = (A *) malloc(sizeof(A));
  }

  /* Once the memory has been allocated for A, initialize members of A. */
  if (this_ptr)
  {
    this_ptr->m_x = x;
    this_ptr->m_z = 0;
  }
  return this_ptr;
}

Complete article: Mapping from C++ classes and methods to C structs and functions.

Destructor representation in C

Destructors also deal with deletion of stack and heap objects.

A::~A()
{
  InformEnd();
}

If the object was allocated on the heap, a boolean is passed to signal to the construction function that the memory was allocated dynamically and needs to be freed.

void A_Destructor(A *this_ptr, BOOLEAN dynamic)
{
  InformEnd(this_ptr);

  /* If the memory was dynamically allocated for A, explicitly free it. */
  if (dynamic)
  {
    free(this_ptr);
  }
}

Complete article: Mapping from C++ classes and methods to C structs and functions.