Hi ,

I want to know, how to implement vtable in C.
This is achieved through virtual functions in C++.

I feel we need to do what c++ compiler internally does, But i am not sure on the entire implementation.

Thanks.

Recommended Answers

All 5 Replies

Here is a simple example of one way to implement vtables in C:

#include <stdio.h>

/* class definitions */
typedef struct Base
{
    void (**vtable)();
    int _x;
} Base;

typedef struct Child
{
    void (**vtable)();
/* begin base class slice */
    int _x;
/* end base class slice */
    int _y;
} Child;

/* class method implementations */
void Base_ToString(Base const* obj) { printf("Base: (%d)\n", obj->_x); }
void Child_ToString(Child const* obj) { printf("Base: (%d,%d)\n", obj->_x, obj->_y); }

/* vtable implementation */
enum { Call_ToString };
void (*Base_Vtable[])() = { &Base_ToString };
void (*Child_Vtable[])() = { &Child_ToString };

/* virtual method implementation */
void ToString(Base const* obj)
{
    obj->vtable[Call_ToString](obj);
}

int main()
{
    /* pick the vtable for objects at compile time */
    Base base = {Base_Vtable, 123};
    Child child = {Child_Vtable, 456, 789};

    Base* a = &base;
    Base* b = (Base*)&child;

    /* call the virtual methods */
    ToString(a);
    ToString(b);
}

I did not include any code for handling multiple inheritance. That is harder to do. The implementation does not matter as long as the end result is the effect of late binding. Even vtables are not required, but it is a common implementation in C++ compilers.

Note: This example is not completely portable, but neither is a compiler. ;)

Implement them as function pointers.

commented: Well said. +6

Can you explain Virtual method implementation in your code a little more

/* virtual method implementation */
void ToString(Base const* obj)
{
    obj->vtable[Call_ToString](obj);
}

My understanding goes something like this,

  1. Concept of Late binding can be acheived by Function pointers or indirect calls - This is due to the fact that compiler cannot simply substitute the address of the function when it encounters a function pointer, instead it needs to dereference it at a later stage.

  2. But in case of c++ compiler will associate a VPTR which points to a VTable while declaring a class when it encounters a virtual function.
    This Vtable is helpfull in 2 ways

                    a. Late Binding.
                    b. Inheritence.
    

Please correct me if I wrong and help me in understanding C version of this above explanation.
I could grasp part of the code posted.

1. Concept of Late binding can be acheived by Function pointers or indirect calls - This is due to the fact that compiler cannot simply substitute the address of the function when it encounters a function pointer, instead it needs to dereference it at a later stage.

That is my understanding too.

2. But in case of c++ compiler will associate a VPTR which points to a VTable while declaring a class when it encounters a virtual function.

Yes. The C++ compiler can generate and assign vtable addresses at compile time, but in C I did it manually for the vtable implementation and picking the vtable for new objects. The virtual method implementation was meant to show that even in C++ there is nothing special about methods. Internally they will probably be nothing more than regular functions with a special naming scheme that ties them to the class, like __Base_void_ToString_void corresponding to void Base::ToString() . If it is a virtual function, instead of using an inline definition, it just makes a call into the vtable using the index of the actual method definition.

My virtual method definition is the end result after an imaginary C++ compilation phase. Think of the original as this:

struct Base
{
    int _x;

    virtual void ToString()
    {
        //...
    }
};

First the method is hoisted out of the struct and made a traditional function with the mangled name and taking this as the first argument:

struct Base
{
    int _x;
};

virtual void __Base_void_ToString_void(Base* this)
{
    //...
}

The method is virtual, so a vtable pointer is created in the struct and a call into the vtable is made in the function definition instead of the inline code. For the example, assume the actual ToString() method index is 0 in the vtable:

struct Base
{
    void (**vtable)();
    int _x;
};

void __Base_void_ToString_void(Base* this)
{
    this->vtable[0](this);
}

Then the actual method definition is pasted into an implementation function and the vtable itself is created, with pointers to the implementation function. This vtable will be assigned to objects of the Base type when they are created:

struct Base
{
    void (**vtable)();
    int _x;
};

void __Base_void_ToString_void(Base* this)
{
    this->vtable[0](this);
}

void __Base_void_ToString__impl_void(Base* this)
{
    //...
}

void (*__Base_vtable[])() = { &__Base_void_ToString__impl_void };

Thanks a Lot for all your explanation and code.
It helped me a lot.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.