Note: This question is purely about asm.js not about C++ nor any other programming language.
As the title already says:
How should a function pointer be implemented in a efficient way?
I couldn't find anything on the web, so I figured asking it here.
Edit:
I would like to implement virtual functions in the compiler I'm working on.
In C++ I would do something like this to generate a vtable:
#include <iostream>
class Base {
public:
virtual void doSomething() = 0;
};
class Derived : public Base {
public:
void doSomething() {
std::cout << "I'm doing something..." << std::endl;
}
};
int main()
{
Base* instance = new Derived();
instance->doSomething();
return 0;
}
To be more precise; how can I generate a vtable in asm.js without the need of plain JavaScript?
In any case, I would like the "near native" capabilities of asm.js while using function pointers.
The solution may be suitable for computer generated code only.
Looking over how asm.js works, I believe your best bet would be to use the method the original CFront compiler used: compile the virtual methods down to functions that take a this pointer, and use thunks to correct the this pointer before passing it. I'll go through it step by step:
No Inheritance
Reduce methods to special functions:
void ExampleObject::foo( void );
would be transformed into
void exampleobject_foo( ExampleObject* this );
This works fine for non-inheritance based objects.
Single Inheritance
We can easily add support for arbitrary large amount of single inheritance through a simple trick: always store the object in memory base first:
class A : public B
would become, in memory:
[[ B ] A ]
Getting closer!
Multiple Inheritance
Now, multiple inheritance makes this much harder to work with
class A : public B, public C
It's impossible for both B and C to be at the start of A; they simply cannot co-exist. There are two choices:
Store an explicit offset (known as delta) for each call to base.
Do not allow calls through A to B or C
The second choice is much preferable for a variety of reasons; if you are calling a base class member function, it's rare you would want to do it through a derived class. Instead, you could simply go C::conlyfunc, which could then do the adjustment to your pointer for you at no cost. Allowing A::conlyfunc removes important information that the compiler could have used, at very little benefit.
The first choice is used in C++; all multiple inheritance objects call a thunk before each call to a base class, which adjusts the this pointer to point to the subobject inside it. In a simple example:
class ExampleBaseClass
{
void foo( void );
}
class ExampleDerivedClass : public ExampleBaseClass, private IrrelevantBaseClass
{
void bar( void );
}
would then become
void examplebaseclass_foo( ExampleBaseClass* this );
void examplederivedclass_bar( ExampleDerivedClass* this);
void examplederivedclass_thunk_foo( ExampleDerivedClass* this)
{
examplebaseclass_foo( this + delta );
}
This could be inlined in many situations, so it's not too big of overhead. However, if you could never refer to ExampleBaseClass::foo as ExampleDerivedClass::foo, these thunks wouldn't be needed, as the delta would be easily discernible from the call itself.
Virtual functions
Virtual functions adds a whole new layer of complexity. In the multiple inheritance example, the thunks had fixed addresses to call; we were just adjusting the this before passing it to an already known function. With virtual functions, the function we're calling is unknown; we could be overridden by a derived object we have no possibility of knowing about at compile time, due to it being in another translation unit or a library, etc.
This means we need some form of dynamic dispatch for each object that has a virtually overridable function; many methods are possible, but C++ implementations tend to use a simple array of function pointers, or a vtable. To each object that has virtual functions, we add a point to an array as a hidden member, usually at the front:
class A
{
hidden:
void* vtable;
public:
virtual void foo( void );
}
We add thunk functions which redirect to the vtable
void a_foo( A* this )
{
int vindex = 0;
this->vtable[vindex](this);
}
The vtable is then populated with pointers to the functions we actually want to call:
vtable[0] = &A::foo_default; // our baseclass implimentation of foo
In a derived class, if we wish to override this virtual function, all we need to do is change the vtable in our own object, to point to the new function, and it will override in the base class as well:
class B: public A
{
virtual void foo( void );
}
will then do this in the constructor:
((A*)this)->vtable[0] = &B::foo;
Finally, we have support for all forms of inheritance!
Almost.
Virtual Inheritance
There is one final caveat with this implementation: if you continue to allow Derived::foo to be used when what you really mean is Base::foo, you get the diamond problem:
class A : public B, public C;
class B : public D;
class C : public D;
A::DFunc(); // Which D?
This problem can also occur when you use base classes as stateful classes, or when you put function that should be has-a rather than is-a; generally, it's a sign of a need for a restructure. But not always.
In C++, this has a solution that is not very elegant, but works:
class A : public B, public C;
class B : virtual D;
class C : virtual D;
This requires those who implement such classes and hierarchies to think ahead and intentionally make their classes a little slower, to support a possible future usage. But it does solve the problem.
How can we implement this solution?
[ [ D ] [ B ] [ Dptr ] [ C ] [ Dptr ] A ]
Rather than use the base class directly, as in normal inheritance, with virtual inheritance we push all usages of D through a pointer, adding an indirection, while stomping the multiple instantiations into a single one. Notice that both B and C have their own pointer, that both point to the same D; this is because B and C don't know if they are free floating copies or bound in derived objects. The same calls need to be used for both, or virtual functions won't work as expected.
Summary
Transform method calls into function calls with a special this parameter in base classes
Structure objects in memory so single inheritance is no different from no inheritance
Add thunks to adjust this pointers then call base classes for multiple inheritance
Add vtables to classes with virtual methods, and make all calls to methods go through vtable to method (thunk -> vtable -> method)
Deal with virtual inheritance through a pointer-to-baseobject rather than derive object calls
All of this is straightforward in js.asm.
Tim,
I'm by no means an asm.js expert but your question intrigues me. It goes to the hart of object oriented language design. It also seems ironic that you are recreating machine-level problems in the javascript domain.
The solution to your question it seems to me is that you will need to setup an accounting of defined types and functions. In Java this is typically done by decorating bytecode with identifiers that represent the correct class->function mapping of any given object. If you use a Int32 identifier for each class that is defined and an additionaly Int32 identifier for each function defined you can then store these in the object representations on the heap. Your vtable is then no more than the mapping of these combination to specific functions.
I hope this helps you.
I am not very familiar with the exact syntax of asm.js, but here is how I implemented a vtable in a compiler of mine (for x86):
Each object are derived from a struct like this:
struct Object {
VTable *vtable;
};
Then the other types I use will look something like this in c++-syntax:
struct MyInt : Vtable {
int value;
};
which is (in this case) equivalent to:
struct MyInt {
VTable *vtable;
int value;
};
So the final layout of the objects are that at offset 0, I know I have a pointer to a vtable. The vtable I use is simply an array of function pointers, again in C-syntax the type VTable could be defined as follows:
typedef Function *VTable;
Where in C i would use void * instead of Function *, since the actual types of the function will vary. What is left for the compiler to do is:
1: For each type containing virtual functions, create a global vtable and populate it with function pointers to the overridden functions.
2: When an object is created, set the vtable member of the object (at offset 0) to point to the global vtable.
Then when you want to call virtual functions you can do something like this:
(*myObject->vtable[1])(1);
to call the function your compiler has assigned the ID 1 in the vtable (methodB in the example below).
A final example: Let's say we have the following two classes:
class A {
public:
virtual int methodA(int) { ... }
virtual int methodB(int) { ... }
virtual int methodC(int) { ... }
};
class B : public A {
public:
virtual int methodA(int) { ... }
virtual int methodB(int) { ... }
};
The VTable for class A and B can look like this:
A: B:
0: &A::methodA 0: &B::methodA
1: &A::methodB 1: &B::methodB
2: &A::methodC 2: &A::methodC
By using this logic, we know that when we are calling methodB on any type derived from A, we shall call whatever function is located at index 1 in the vtable of that object.
Of course, this solution do not work right away if you want to allow for multiple inheritance, but I am fairly sure it can be extended to do so. After some debugging with Visual Studio 2008, it seems like this is more or less how the vtables are implemented there (of course there it is extended to handle multiple inheritance, I have not tried to figure that out yet).
I hope you get some ideas that can be applied in asm.js at least. Like I said, I do not know exactly how asm.js works, but I have managed to implement this system in x86 assembly and I do not see any issues with implementing it in JavaScript either, so I hope it can be used in asm.js as well.
Related
I'm pretty experienced in C++, but I find myself struggling with this particular design problem.
I want to have a base class, that I can stuff in a std::map, with a virtual function that can be called generically by a method that is querying the map. But I want to be able to call that function from a base class pointer with different parameters, depending on what the derived type is. Something functionally similar to the following wildly illegal example:
class Base
{
virtual void doThing() = 0;
}
class Derived1 : public Base
{
void doThing(int i, const std::string& s) {} // can't do that
}
class Derived2: public Base
{
void doThing(double d, std::vector<int>& v) {} // can't do that either
}
enum class ID = {
DERIVED1,
DERIVED2
}
std::map<ID, std::unique_ptr<Base> thingmap = { ... }
std::unique_ptr<Base>& getThing(int) { return thingmap[i] };
int main(int I, const char* argv[]) {
auto baseptr = getThing(DERIVED1);
baseptr->doThing(42, "hello world");
}
I don't want the caller to have to know what the derived type is, only that a Derived1 takes an int and a string. Downcasting isn't an option because the whole point of this is that I don't want the caller to have to specify the derived type explicitly. And C-style variable argument lists are yucky. :-)
Edited to clarify: I know exactly why the above can't possibly work, thank you. :-) This is library code and I'm trying to conceal the internals of the library from the caller to the greatest extent possible. If there's a solution it probably involves a variadic template function.
You can't do that.
Your map is filled with Base instances, so the class DO NOT have the required prototypes implemented in Derived1 or Derived2... And redefining overloaded methods do not implement the pure virtual method doThing, so Derived1 and Derived2 are still abstract classes and therefore cannot be instanciated.
Worst, your getThing function only deals with Base, so the compiler would NEVER allows you to use the overloaded signatures, since they don't exist AT ALL in Base. There is nothing to know the real class behind, since you don't use templates and implicit template argument deduction.
Your pattern cannot be done this way, period. Since you don't want to use neither downcasting nor explicitely specified child classes, you're stuck.
Even if you add all possible prototypes in Base, since it will be pure virtual methods, both derived classes will still be abstract classes. And if they aren't, then you'll never be able to know which one is a NOP and which one is implemented, since it will requires downcasting!
I think that you made a common mistake, even done by expert developers sometimes: you went into conception directly, BEFORE determining your real ROOT needs.
What you ask looks like the core system of a factory, and it's really not the good way to implement this design pattern and/or designing the specialized derived classes.
So, vtable is a table maintained by the compiler which contains function pointers that point to the virtual functions in that class.
and
Assigning a derived class's object to an ancestor class's object is called up-casting.
Up-casting is handling a derived class instance/object using a base class pointer or reference; the objects are not "assigned to", which implies an overwriting of value ala operator= invocation.
(Thanks to: Tony D)
Now, how it is known at run time "which" class's virtual function is supposed to be called?
Which entry in vtable refers to the function of "particular" derived classes which is supposed to be called at run time?
You can imagine (although the C++ specification doesn't say this) that the vtable is an identifier (or some other metadata that can be used to "find more information" about the class itself) and a list of functions.
So, if we have a class like this:
class Base
{
public:
virtual void func1();
virtual void func2(int x);
virtual std::string func3();
virtual ~Base();
... some other stuff we don't care about ...
};
The compiler will then produce a VTable something like this:
struct VTable_Base
{
int identifier;
void (*func1)(Base* this);
void (*func2)(Base* this, int x);
std::string (*func3)(Base* this);
~Base(Base *this);
};
The compiler will then create an internal structure that, something like this (this is not possible to compile as C++, it's just to show what the compiler actually does - and I call it Sbase to differntiate the actual class Base)
struct SBase
{
VTable_Base* vtable;
inline void func1(Base* this) { vtable->func1(this); }
inline void func2(Base* this, int x) { vtable->func2(this, x); }
inline std::string func3(Base* this) { return vtable->func3(this); }
inline ~Base(Base* this) { vtable->~Base(this); }
};
It also builds the real vtable:
VTable_Base vtable_base =
{
1234567, &Base::func1, &Base::func2, &Base::func3, &Base::~Base
};
And in the constructor for Base, it will set the vtable = vtable_base;.
When we then add a derived class, where we override one function (and by default, the destructor, even if we don't declare one) :
class Derived : public Base
{
virtual void func2(int x) override;
};
The compiler will now make this structure:
struct VTable_Derived
{
int identifier;
void (*func1)(Base* this);
void (*func2)(Base* this, int x);
std::string (*func3)(Base* this);
~Base(Derived *this);
};
and then does the same "structure" building:
struct SDerived
{
VTable_Derived* vtable;
inline void func1(Base* this) { vtable->func1(this); }
inline void func2(Base* this, int x) { vtable->func2(this, x); }
inline std::string func3(Base* this) { return vtable->func3(this); }
inline ~Derived(Derived* this) { vtable->~Derived(this); }
};
We need this structure for when we are using Derived directly rather than through the Base class.
(We rely on the compiler chainin the ~Derived to call ~Base too, just like normal destructors that inherit)
And finally, we build an actual vtable:
VTable_Derived vtable_derived =
{
7654339, &Base::func1, &Derived::func2, &Base::func3, &Derived::~Derived
};
And again,the Derived constructor will set Dervied::vtable = vtable_derived for all instances.
Edit to answer question in comments: The compiler has to carefully place the various components in both VTable_Derived and SDerived such that it matches VTable_Base and SBase, so that when we have a pointer to Base, the Base::vtable and Base::funcN() are matching Derived::vtable and Derived::FuncN. If that doesn't match up, then the inheritance won't work.
If new virtual functions are added to Derived, they must then be placed after the ones inherited from Base.
End Edit.
So, when we do:
Base* p = new Derived;
p->func2();
the code will look up SBase::Func2, which will use the correct Derived::func2 (because the actual vtable inside p->vtable is VTable_Derived (as set by the Derived constructor that is called in conjunction with the new Derived).
I'll take a different route from the other answers and try to fill just the specific gaps in your knowledge, without going very much into the details. I'll address the mechanics just enough to help your understanding.
So, vtable is a table maintained by the compiler which contains function pointers that point to the virtual functions in that class.
The more precise way to say this is as follows:
Every class with virtual methods, including every class that inherits from a class with virtual methods, has its own virtual table. The virtual table of a class points to the virtual methods specific to that class, i.e. either inherited methods, overridden methods or newly added methods. Every instance of such a class contains a pointer to the virtual table that matches the class.
Up-casting is handling a derived class instance/object using a base class pointer or reference; (...)
Perhaps more enlightening:
Up-casting means that a pointer or reference to an instance of class Derived is treated as if it were a pointer or reference to an instance of class Base. The instance itself, however, is still purely an instance of Derived.
(When a pointer is "treated as a pointer to Base", that means that the compiler generates code for dealing with a pointer to Base. In other words, the compiler and the generated code know no better than that they are dealing with a pointer to Base. Hence, a pointer that is "treated as" will have to point to an object that offers at least the same interface as instances of Base. This happens to be the case for Derived because of inheritance. We'll see how this works out below.)
At this point we can answer the first version of your question.
Now, how it is known at run time "which" class's virtual function is supposed to be called?
Suppose we have a pointer to an instance of Derived. First we upcast it, so it is treated as a pointer to an instance of Base. Then we call a virtual method upon our upcasted pointer. Since the compiler knows that the method is virtual, it knows to look for the virtual table pointer in the instance. While we are treating the pointer as if it points to an instance of Base, the actual object has not changed value and the virtual table pointer within it is still pointing to the virtual table of Derived. So at runtime, the address of the method is taken from the virtual table of Derived.
Now, the particular method may be inherited from Base or it might be overridden in Derived. It does not matter; if inherited, the method pointer in the virtual table of Derived simply contains the same address as the corresponding method pointer in the virtual table of Base. In other words, both tables are pointing to the same method implementation for that particular method. If overridden, the method pointer in the virtual table of Derived differs from the corresponding method pointer in the virtual table of Base, so method lookups on instances of Derived will find the overridden method while lookups on instances of Base will find the original version of the method — regardless of whether a pointer to the instance is treated as a pointer to Base or a pointer to Derived.
Finally, it should now be straightforward to explain why the second version of your question is a bit misguided:
Which entry in vtable refers to the function of "particular" derived classes which is supposed to be called at run time?
This question presupposes that vtable lookups are first by method and then by class. It is the other way round: first, the vtable pointer in the instance is used to find the vtable for the right class. Then, the vtable for that class is used to find the right method.
Which entry in vtable refers to the function of "particular" derived
classes which is supposed to be called at run time?
None, it is not an entry in the vtable, but the vtable pointer that is part of each and every object instance that determines which are the correct set of virtual functions for that particular object. This way, depending on the actual vtable pointed to, invoking the "first virtual method" from the vtable may result in the calling of different functions for objects of different types in the same polymorphic hierarchy.
Implementations may vary, but what I personally consider the most logical and performing thing to do is to have the vtable pointer being the first element in the class layout. This way you can dereference the very address of the object to determine its type based on the value of the pointer sitting in that address, since all objects of a given type will have that pointer pointing to the same vtable, which is created uniquely for every object that has virtual methods, which is required to enable features as overriding certain virtual methods.
How do upcasting and vtables work together to ensure correct dynamic
binding?
Upcasting itself isn't strictly needed, neither is downcasting. Remember that you already have the object allocated in memory, and it will already have its vtable pointer set to the correct vtable for that type which is what ensures it, up an down casting doesn't change the vtable for that object, it only changes the pointer you operate through.
Downcasting is needed when you want to access functionality that is not available in the base class and is declared in the derived class. But before you try to do that, you must be sure that particular object is of or inherits the type which declares that functionality, which is where dynamic_cast comes in, when you dynamic cast the compiler generates a check for that vtable entry and whether it inherits the requested type from another table, generated at compile time, and if so the dynamic cast succeeds, otherwise it fails.
The pointer you access the object through doesn't refer to the right set of virtual functions to call, it merely serves as a gauge to which functions in the vtable you can refer to as the developer. That is why it is safe to upcast using a C style or static cast, which performs no runtime checks, because then you only limit your gauge to the functions available in the base class, which are already available in the derived class, so there is no room for error and harm. And that's why you must always use a dynamic cast or some other custom technique still based on virtual dispatch when you downcast, because you have to be sure that object's associated vtable does indeed contain the extra functionality you may invoke.
Otherwise you will get undefined behavior, and of the "bad kind" at that, meaning something fatal will most likely happen, since interpreting arbitrary data as an address of a function of particular signature to be called is a very big no-no.
Also note that in a static context, i.e. when it is known at compile time what the type is, the compiler will most likely not use the vtable to call virtual functions but use direct static calls or even inline certain functions, which will make them that much faster. In such cases upcasting and using a base class pointer instead of the actual object will only diminish that optimization.
Polymorphism and Dynamic Dispatch (hyper-abridged version)
Note: I was not able to fit enough information about multiple inheritance with virtual bases, as there is not much of anything simple about it, and the details would clutter the exposition (further). This answer demonstrates the mechanisms used to implement dynamic dispatch assuming only single inheritance.
Interpreting abstract types and their behaviors visible across module boundaries requires a common Application Binary Interface (ABI). The C++ standard, of course, does not require the implementation of any particular ABI.
An ABI would describe:
The layout of virtual method dispatch tables (vtables)
The metadata required for runtime type checks and cast operations
Name decoration (a.k.a. mangling), calling conventions, and many other things.
Both modules in the following example, external.so and main.o, are assumed to have been linked to the same runtime. Static and dynamic binding give preference to symbols located within the calling module.
An external library
external.h (distributed to users):
class Base
{
__vfptr_t __vfptr; // For exposition
public:
__attribute__((dllimport)) virtual int Helpful();
__attribute__((dllimport)) virtual ~Base();
};
class Derived : public Base
{
public:
__attribute__((dllimport)) virtual int Helpful() override;
~Derived()
{
// Visible destructor logic here.
// Note: This is in the header!
// __vft#Base gets treated like any other imported symbol:
// The address is resolved at load time.
//
this->__vfptr = &__vft#Base;
static_cast<Base *>(this)->~Base();
}
};
__attribute__((dllimport)) Derived *ReticulateSplines();
external.cpp:
#include "external.h" // the version in which the attributes are dllexport
__attribute__((dllexport)) int Base::Helpful()
{
return 47;
}
__attribute__((dllexport)) Base::~Base()
{
}
__attribute__((dllexport)) int Derived::Helpful()
{
return 4449;
}
__attribute__((dllexport)) Derived *ReticulateSplines()
{
return new Derived(); // __vfptr = &__vft#Derived in external.so
}
external.so (not a real binary layout):
__vft#Base:
[offset to __type_info#Base] <-- in external.so
[offset to Base::~Base] <------- in external.so
[offset to Base::Helpful] <----- in external.so
__vft#Derived:
[offset to __type_info#Derived] <-- in external.so
[offset to Derived::~Derived] <---- in external.so
[offset to Derived::Helpful] <----- in external.so
Etc...
__type_info#Base:
[null base offset field]
[offset to mangled name]
__type_info#Derived:
[offset to __type_info#Base]
[offset to mangled name]
Etc...
An application using the external library
special.hpp:
#include <iostream>
#include "external.h"
class Special : public Base
{
public:
int Helpful() override
{
return 55;
}
virtual void NotHelpful()
{
throw std::exception{"derp"};
}
};
class MoreDerived : public Derived
{
public:
int Helpful() override
{
return 21;
}
~MoreDerived()
{
// Visible destructor logic here
this->__vfptr = &__vft#Derived; // <- the version in main.o
static_cast<Derived *>(this)->~Derived();
}
};
class Related : public Base
{
public:
virtual void AlsoHelpful() = 0;
};
class RelatedImpl : public Related
{
public:
void AlsoHelpful() override
{
using namespace std;
cout << "The time for action... Is now!" << endl;
}
};
main.cpp:
#include "special.hpp"
int main(int argc, char **argv)
{
Base *ptr = new Base(); // ptr->__vfptr = &__vft#Base (in external.so)
auto r = ptr->Helpful(); // calls "Base::Helpful" in external.so
// r = 47
delete ptr; // calls "Base::~Base" in external.so
ptr = new Derived(); // ptr->__vfptr = &__vft#Derived (in main.o)
r = ptr->Helpful(); // calls "Derived::Helpful" in external.so
// r = 4449
delete ptr; // calls "Derived::~Derived" in main.o
ptr = ReticulateSplines(); // ptr->__vfptr = &__vft#Derived (in external.so)
r = ptr->Helpful(); // calls "Derived::Helpful" in external.so
// r = 4449
delete ptr; // calls "Derived::~Derived" in external.so
ptr = new Special(); // ptr->__vfptr = &__vft#Special (in main.o)
r = ptr->Helpful(); // calls "Special::Helpful" in main.o
// r = 55
delete ptr; // calls "Base::~Base" in external.so
ptr = new MoreDerived(); // ptr->__vfptr = & __vft#MoreDerived (in main.o)
r = ptr->Helpful(); // calls "MoreDerived::Helpful" in main.o
// r = 21
delete ptr; // calls "MoreDerived::~MoreDerived" in main.o
return 0;
}
main.o:
__vft#Derived:
[offset to __type_info#Derivd] <-- in main.o
[offset to Derived::~Derived] <--- in main.o
[offset to Derived::Helpful] <---- stub that jumps to import table
__vft#Special:
[offset to __type_info#Special] <-- in main.o
[offset to Base::~Base] <---------- stub that jumps to import table
[offset to Special::Helpful] <----- in main.o
[offset to Special::NotHelpful] <-- in main.o
__vft#MoreDerived:
[offset to __type_info#MoreDerived] <---- in main.o
[offset to MoreDerived::~MoreDerived] <-- in main.o
[offset to MoreDerived::Helpful] <------- in main.o
__vft#Related:
[offset to __type_info#Related] <------ in main.o
[offset to Base::~Base] <-------------- stub that jumps to import table
[offset to Base::Helpful] <------------ stub that jumps to import table
[offset to Related::AlsoHelpful] <----- stub that throws PV exception
__vft#RelatedImpl:
[offset to __type_info#RelatedImpl] <--- in main.o
[offset to Base::~Base] <--------------- stub that jumps to import table
[offset to Base::Helpful] <------------- stub that jumps to import table
[offset to RelatedImpl::AlsoHelpful] <-- in main.o
Etc...
__type_info#Base:
[null base offset field]
[offset to mangled name]
__type_info#Derived:
[offset to __type_info#Base]
[offset to mangled name]
__type_info#Special:
[offset to __type_info#Base]
[offset to mangled name]
__type_info#MoreDerived:
[offset to __type_info#Derived]
[offset to mangled name]
__type_info#Related:
[offset to __type_info#Base]
[offset to mangled name]
__type_info#RelatedImpl:
[offset to __type_info#Related]
[offset to mangled name]
Etc...
Invocation is (or might not be) Magic!
Depending on the method and what can be proven at the binding side, a virtual method call may be bound statically or dynamically.
A dynamic virtual method call will read the target function's address from the vtable pointed to by a __vfptr member.
The ABI describes how functions are ordered in vtables. For example: They might be ordered by class, then lexicographically by mangled name (which includes information about const-ness, parameters, etc...). For single inheritance, this approach guarantees that a function's virtual dispatch index will always be the same, regardless of how many distinct implementations there are.
In the examples given here, destructors are placed at the beginning of each vtable, if applicable. If the destructor is trivial and non-virtual (not defined or does nothing), the compiler may elide it entirely, and not allocate a vtable entry for it.
Base *ptr = new Special{};
MoreDerived *md_ptr = new MoreDerived{};
// The cast below is checked statically, which would
// be a problem if "ptr" weren't pointing to a Special.
//
Special *sptr = static_cast<Special *>(ptr);
// In this case, it is possible to
// prove that "ptr" could point only to
// a Special, binding statically.
//
ptr->Helpful();
// Due to the cast above, a compiler might not
// care to prove that the pointed-to type
// cannot be anything but a Special.
//
// The call below might proceed as follows:
//
// reg = sptr->__vptr[__index_of#Base::Helpful] = &Special::Helpful in main.o
//
// push sptr
// call reg
// pop
//
// This will indirectly call Special::Helpful.
//
sptr->Helpful();
// No cast required: LSP is satisfied.
ptr = md_ptr;
// Once again:
//
// reg = ptr->__vfptr[__index_of#Base::Helpful] = &MoreDerived::Helpful in main.o
//
// push ptr
// call reg
// pop
//
// This will indirectly call MoreDerived::Helpful
//
ptr->Helpful();
The logic above is the same for any invocation site that requires dynamic binding. In the example above, it doesn't matter exactly what type ptr or sptr point to; the code will just load a pointer at a known offset, then blindly call it.
Type casting: Ups and Downs
All information about a type hierarchy must be available to the compiler when translating a cast or function call expression. Symbolically, casting is just a matter of traversing a directed graph.
Up-casting in this simple ABI can be performed entirely at compile time. The compiler needs only to examine the type hierarchy to determine if the source and target types are related (there is a path from the source to the target in the type graph). By the substitution principle, a pointer to a MoreDerived also points to a Base and can be interpreted as such. The __vfptr member is at the same offset for all types in this hierarchy, so RTTI logic doesn't need to handle any special cases (in certain implementations of VMI, it would need to grab another offset from a type thunk to grab another vptr and so on...).
Down-casting, however, is different. Since casting from a base type to a derived type involves determining if the pointed-to object has a compatible binary layout, it is necessary to perform an explicit type check (conceptually, this is "proving" that the extra information exists beyond the end of the structure assumed at compile time).
Note that there are multiple vtable instances for the Derived type: One in external.so and one in main.o. This is because a virtual method defined for Derived (its destructor) appears in every translation unit that includes external.h.
Even though the logic is identical in both cases, both images in this example need to have their own copy. This is why type checking cannot be performed using addresses alone.
A down-cast is then performed by walking a type graph (copied in both images) starting from the source type decoded at runtime, comparing mangled names until the compile-time target is matched.
For example:
Base *ptr = new MoreDerived();
// ptr->__vfptr = &__vft::MoreDerived in main.o
//
// This provides the code below with a starting point
// for dynamic cast graph traversals.
// All searches start with the type graph in the current image,
// then all other linked images, and so on...
// This example is not exhaustive!
// Starts by grabbing &__type_info#MoreDerived
// using the offset within __vft#MoreDerived resolved
// at load time.
//
// This is similar to a virtual method call: Just grab
// a pointer from a known offset within the table.
//
// Search path:
// __type_info#MoreDerived (match!)
//
auto *md_ptr = dynamic_cast<MoreDerived *>(ptr);
// Search path:
// __type_info#MoreDerived ->
// __type_info#Derived (match!)
//
auto *d_ptr = dynamic_cast<Derived *>(ptr);
// Search path:
// __type_info#MoreDerived ->
// __type_info#Derived ->
// __type_info#Base (no match)
//
// Did not find a path connecting RelatedImpl to MoreDerived.
//
// rptr will be nullptr
//
auto *rptr = dynamic_cast<RelatedImpl *>(ptr);
At no point in the code above did ptr->__vfptr need to change. The static nature of type deduction in C++ requires the implementation to satisfy the substitution principle at compile time, meaning that the actual type of an object cannot change at runtime.
Summary
I've understood this question as one about the mechanisms behind dynamic dispatch.
To me, "Which entry in vtable refers to the function of "particular" derived classes which is supposed to be called at run time?", is asking how a vtable works.
This answer is intended to demonstrate that type casting affects only the view of an object's data, and that the implementation of dynamic dispatch in these examples operate independently of it. However, type casting does affect dynamic dispatch in the case of multiple inheritance, where determining which vtable to use may require multiple steps (an instance of a type with multiple bases may have multiple vptrs).
casting
casting is a concept associated with variable. So any variable can be casted. It can be casted up or down.
char charVariable = 'A';
int intVariable = charVariable; // upcasting
int intVariable = 20;
char charVariale = intVariable; // downcasting
for system defined data type Up cast or downcast is based on your current variable and it mainly related to how much memory compiler is allocating to both compared variable.
If you are assigning a variable which is allocating less memory than the type what is converting to, is called up cast.
If you are assigning a variable which is allocating more memory than the type what is converting to, is called down cast.
Down cast create some problem when the value is trying to cast can't fit in to that allocated memory area.
Upcasting in Class level
Just like system defined data type we can have object of base class and derived class. So if we want to convert derived type to base type , it is known as down upcasting. That can be achieved by pointer of a base class pointing to a derived class type.
class Base{
public:
void display(){
cout<<"Inside Base::display()"<<endl;
}
};
class Derived:public Base{
public:
void display(){
cout<<"Inside Derived::display()"<<endl;
}
};
int main(){
Base *baseTypePointer = new Derived(); // Upcasting
baseTypePointer.display(); // because we have upcasted we want the out put as Derived::display() as output
}
output
Inside Base::display()
Excepted
Inside Derived::display()
In the above scenario the output wasn't as excepted. Its because we don't have the v-table and vptr (virtual pointer) in the object the base pointer will call the Base::display() though we have assigned derived type to the base pointer.
To avoid this problem c++ gives us virtual concept. Now the base class display function need to be changed to a virtual type.
virtual void display()
full code is:
class Base{
public:
virtual void display(){
cout<<"Inside Base::display()"<<endl;
}
};
class Derived:public Base{
public:
void display(){
cout<<"Inside Derived::display()"<<endl;
}
};
int main(){
Base *baseTypePointer = new Derived(); // Upcasting
baseTypePointer.display(); // because we have upcasted we want the out put as Derived::display() as output
}
output
Inside Derived::display()
Excepted
Inside Derived::display()
To understand this we need to understand v-table and vptr;
when ever compiler find a virtual along with a function it will generate a virtual table for each of the classes (both Base and all the derived classes).
If virtual function is present than every object will be containing vptr (virtual pointer) pointing to the respective class vtable and vtable will contain the pointer to the respective class virtual function. when you will call the function throught vptr the virutal function will get called and it will invoke the respective class function and we will achieve the required output.
I believe, this is best explained by implementing polymorphism in C. Given these two C++ classes:
class Foo {
virtual void foo(int);
};
class Bar : public Foo {
virtual void foo(int);
virtual void bar(double);
};
the C structure definitions (i. e. the header file) would look like this:
//For class Foo
typedef struct Foo_vtable {
void (*foo)(int);
} Foo_vtable;
typedef struct Foo {
Foo_vtable* vtable;
} Foo;
//For class Bar
typedef struct Bar_vtable {
Foo_vtable super;
void (*bar)(double);
}
typedef struct Bar {
Foo super;
} Bar;
As you see, there are two structure definitions for each class, one for the vtable and one for the class itself. Note also that both structures for class Bar include a base class object as their first member which allows us upcasting: both (Foo*)myBarPointer and (Foo_vtable*)myBar_vtablePointer are valid. As such, given a Foo*, it is safe to find the location of the foo() member by doing
Foo* basePointer = ...;
(basePointer->vtable->foo)(7);
Now, lets take a look at how we can actually fill the vtables. For that we write some constructors that use some statically defined vtable instances, this is what the foo.c file could look like
#include "..."
static void foo(int) {
printf("Foo::foo() called\n");
}
Foo_vtable vtable = {
.foo = &foo,
};
void Foo_construct(Foo* me) {
me->vtable = vtable;
}
This makes sure that it is possible to execute (basePointer->vtable->foo)(7) on every object that has been passed to Foo_construct(). Now, the code for Bar is quite similar:
#include "..."
static void foo(int) {
printf("Bar::foo() called\n");
}
static void bar(double) {
printf("Bar::bar() called\n");
}
Bar_vtable vtable = {
.super = {
.foo = &foo
},
.bar = &bar
};
void Bar_construct(Bar* me) {
Foo_construct(&me->super); //construct the base class.
(me->vtable->foo)(7); //This will print Foo::foo()
me->vtable = vtable;
(me->vtable->foo)(7); //This will print Bar::foo()
}
I have used static declarations for the member functions to avoid having to invent a new name for each implementation, static void foo(int) restricts the visibility of the function to the source file. However, it can still be called from other files by the use of a function pointer.
Usage of these classes could look like this:
#include "..."
int main() {
//First construct two objects.
Foo myFoo;
Foo_construct(&myFoo);
Bar myBar;
Bar_construct(&myBar);
//Now make some pointers.
Foo* pointer1 = &myFoo, pointer2 = (Foo*)&myBar;
Bar* pointer3 = &myBar;
//And the calls:
(pointer1->vtable->foo)(7); //prints Foo::foo()
(pointer2->vtable->foo)(7); //prints Bar::foo()
(pointer3->vtable->foo)(7); //prints Bar::foo()
(pointer3->vtable->bar)(7.0); //prints Bar::bar()
}
Once you know how this works, you know how C++ vtables work. The only difference is that in C++ the compiler does the work that I did myself in the code above.
Let me try to explain it with some examples:-
class Base
{
public:
virtual void function1() {cout<<"Base :: function1()\n";};
virtual void function2() {cout<<"Base :: function2()\n";};
virtual ~Base(){};
};
class D1: public Base
{
public:
~D1(){};
virtual void function1() { cout<<"D1 :: function1()\n";};
};
class D2: public Base
{
public:
~D2(){};
virtual void function2() { cout<< "D2 :: function2\n";};
};
So, compiler would generate three vtables one for each class as these classes have virtual functions. ( Although it's compiler-dependant ).
NOTE:- vtables contain only pointers to virtual functions. Non-virtual functions would still be resolved at compile time...
You are right in saying that vtables are nothing just pointers to functions. vtables for these classes would be like something:-
vtable for Base:-
&Base::function1 ();
&Base::function2 ();
&Base::~Base ();
vtable for D1:-
&D1::function1 ();
&Base::function2 ();
&D1::~D1();
vtable for D2:-
&Base::function1 ();
&D2::function2 ();
&D2::~D2 ();
vptr is a pointer which is used for look-up purpose on this table. Each object of polymorphic class has extra allocated space for vptr in it ( Although where vptr would be in object is entirely implementation dependant ).Generally vptr is at the beginning of object.
With taking all into account , if I make a call to func, compiler at run time would check what b is actually pointing to:-
void func ( Base* b )
{
b->function1 ();
b->function2 ();
}
Let's say we have object of D1 passed to func. Compiler would resolve calls in following manner:-
First it would fetch vptr from object and then it will use it to get correct address of function to call. SO, in this case vptr would give access to D1's vtable. and when it looksup for function1 it would get the address of function1 defined in base class. In case of call to function2, it would get address of base's function2.
Hope I have clarified your doubts to your satisfaction...
The implementation is compiler specific. Here I am going to do some thoughts that have NOTHING TO DO WITH ANY ACTUAL KNOWLEDGE of how exactly it is done in compilers, but just with some minimal requirements that are needed in order to work as required. Keep in mind that each instance of a class with virtual methods knows at run time which is the class it belongs too.
Lets suppose we have a chain of base and derived classes with a length of 10 ( so a derived class has a gran gran ... gran father ).
We may call these classes base0 base1 ... base9 where base9 derives from base8 etc.
Each of these classes define a method as: virtual void doit(){ ... }
Let's suppose that in the base class we use that method inside a method called "dowith_doit" non overridden in any derived class.
The semantics of c++ imply that depending on the base class of the instance we have at hand, we must apply to that instance the "doit" defined in the base class of the instance at hand.
Essentially we have two possible ways of doing it:
a) Assign to any such virtual method a number that must be different for each method defined in the chain of derived classes. In that case the number could be also a hash of the name of the method.
Each class defines a table with 2 columns were the first column holds the number of the method and the second column the address of the function. In that case each class will have a vtable with so many rows as the number of virtual methods defined inside the class.
The execution of the method happens by searching inside the class the method under consideration. That search may be done linearly ( slow ) of by bisections ( when there is an order based on the number of the method).
b) Assign to any such method a progressively increasing integer number (for each different method in the chain of classes), and for each class define a table with only one column. For virtual methods defined inside the class the function address will be in the raw defined by the number of the method. There will be many rows with null pointers because each class doesn't override always the methods of previous classes.
The implementation may choose in order to improve efficiency to fill null rows with the address hold in the ancestor class of the class under consideration.
Essentially no other simple ways exist in order work with virtual methods efficiently.
I suppose that only the second solution (b) is used in actual implementations, because the trade of between space overhead used for non existing methods compared to execution efficiency of case (b) is favorable for case b (taking into consideration too that methods are limited in number - may be 10 20 50 but not 5000 ).
Upon instantiation every class with at least one virtual function gets a hidden member usually called vTable (or virtual dispatch table, VDT).
class Base {
hidden: // not part of the language, just to illustrate.
static VDT baseVDT; // per class VDT for base
VDT *vTable; // per object instance
private:
...
public:
virtual int base1();
virtual int base2();
...
};
The vTable contains pointers to all functions in Base.
As a hidden part of Base's constructor vTable gets assigned to baseVDT.
VDT Base::baseVDT[] = {
Base::base1,
Base::base2
};
class Derived : public Base {
hidden:
static VDT derivedVDT; // per class VDT for derived
private:
...
public:
virtual int base2();
...
};
The vTable for Derived contains pointers to all functions defined in Base followed by functions defined in Derived . When objects of type Derived gets constructed, vTable gets set to derivedVDT.
VDT derived::derivedVDT[] = {
// functions first defined in Base
Base::base1,
Derived::base2, // override
// functions first defined in Derived are appended
Derived::derived3
}; // function 2 has an override in derived.
Now if we have
Base *bd = new Derived;
Derived *dd = new Derived;
Base *bb = new Base;
bd points to an object of type derived who's vTable points to Derived
So the function calls
x = bd->base2();
y = bb->base2();
actually is
// "base2" here is the index into vTable for base2.
x = bd->vTable["base2"](); // vTable points to derivedVDT
y = bb->vTable["base2"](); // vTable points to baseVDT
The index is the same in both due to the construction of the VDT. This also means the compiler knows the index at the moment of compilation.
This could also be implemented as
// call absolute address to virtual dispatch function which calls the right base2.
x = Base::base2Dispatch(bd->vTable["base2"]);
inline Base::base2Dispatch(void *call) {
return call(); // call through function pointer.
}
Which with O2 or O3 will be the same.
There are some special cases:
dd points to a derived or more derived object and base2 is declared final then
z = dd->base2();
actually is
z = Derived::base2(); // absolute call to final method.
If dd pointed to a Base object or anything else your in undefined behaviour land and the compiler can still do this.
The other case is if the compiler sees there are only a few derived classes from Base it could generate a Oracle interface for base2. [free after a MS or Intel compiler guy at some C++ conference in 2012 or 2013? showing that (~500%?) more code gives (2+ times?) speedup on average]
inline Base::base2Dispatch(void *call) {
if (call == Derived::base2) // most likely from compilers static analysis or profiling.
return Derived::base2(); // call absolute address
if (call == Base::base2)
return Base::base2(); // call absolute address
// Backup catch all solution in case of more derived classes
return call(); // call through function pointer.
}
Why on earth do you want to do this as a compiler??? more code is bad, unneeded branches diminish performance!
Because calling a function pointer is very slow on many architectures, optimistic example
Get the address from memory, 3+ cycles.
Delayed pipeline while waiting for ip value, 10 cycles, on some processors 19+ cycles.
If the most complex modern cpu's can predict the actual jump address [BTB] as well as it does branch prediction, this might be a loss. Else the ~8 extra instructions will easily save the 4*(3+10) instructions lost due to pipeline stalls (if the prediction failure rate is less than 10-20%).
If the branches in the two if's both predict taken (ie evaluate to false) the ~2 cycles lost is nicely covered by the memory latency to get the call address and we are no worse off.
If one of the if's are mispredicts the the BTB will most likely also be wrong. Then the cost of the mispredicts is around 8 cycles of which 3 are paid by the memory latency, and the correct not take or the 2nd if might save the day or we pay the full 10+ pipeline stall.
If only the 2 possibilities exists one of them will be taken and we save the pipeline stall from the function pointer call and we will max. get one mispredict resulting in no (significant) worse performance than calling directly.
If the memory delay is longer and the result is correctly predicted the effect is much larger.
C++ supports inheritance.
But how is it implemented in the compiler?
Does the compiler copy and paste all the implementation from parent to child?
EXTREMELY simplified, if we are talking about something like this:
class A
{
public:
int func1() { do something; }
int func2() { do something; }
};
class B : public A
{
public:
int func2() { do somethign else; }
};
B b;
b.func1();
then what happens inside the compiler will be this (remember, this is VERY simplified, and the real compiler code will be a lot more complex, I'm sure):
... fname = "func1" from the source code ...
... object = "b";
function fn;
while (!(fn = find_func(object, fname)))
object = parent_object(object);
if (fn)
produce_call(fn);
else
print_error_not_found(fname);
If we are talking about virtual functions, then the compiler will produce a table which holds the address of the respective virtual function, and the table is generated for each class, based on a similar principle of "find the function that exists in a this class or one of its parents).
[In the above, I've ignored the fact that one class can have more than one "parent" class - it doesn't change how things works, just that the code has to maintain a list or array of "more classes at the same level"]
Just like member variables, base classes cause a subobject to be embedded inside all instances of the derived class. Member functions of the base class are not duplicated for the derived class, instead they are called on this subobject corresponding to the base class.
The compiler knows where this subobject is located relative to the full object, and will insert pointer arithmetic everywhere there is a cast (possibly implicit) between pointer (or reference) to derived and to base. This includes the hidden this-pointer arguments passed to member functions of the base type.
Virtual inheritance is a little bit tricky, because the offset can be different depending on the most-derived type. In that case, the compiler needs to store the offset as a variable inside the class instances so it can be looked up at runtime (just like pointers to virtual member functions, there might be another layer of indirection involved to save space).
Just what the topic asks. Also want to know why non of the usual examples of CRTP do not mention a virtual dtor.
EDIT:
Guys, Please post about the CRTP prob as well, thanks.
Only virtual functions require dynamic dispatch (and hence vtable lookups) and not even in all cases. If the compiler is able to determine at compile time what is the final overrider for a method call, it can elide performing the dispatch at runtime. User code can also disable the dynamic dispatch if it so desires:
struct base {
virtual void foo() const { std::cout << "base" << std::endl; }
void bar() const { std::cout << "bar" << std::endl; }
};
struct derived : base {
virtual void foo() const { std::cout << "derived" << std::endl; }
};
void test( base const & b ) {
b.foo(); // requires runtime dispatch, the type of the referred
// object is unknown at compile time.
b.base::foo();// runtime dispatch manually disabled: output will be "base"
b.bar(); // non-virtual, no runtime dispatch
}
int main() {
derived d;
d.foo(); // the type of the object is known, the compiler can substitute
// the call with d.derived::foo()
test( d );
}
On whether you should provide virtual destructors in all cases of inheritance, the answer is no, not necessarily. The virtual destructor is required only if code deletes objects of the derived type held through pointers to the base type. The common rule is that you should
provide a public virtual destructor or a protected non-virtual destructor
The second part of the rule ensures that user code cannot delete your object through a pointer to the base, and this implies that the destructor need not be virtual. The advantage is that if your class does not contain any virtual method, this will not change any of the properties of your class --the memory layout of the class changes when the first virtual method is added-- and you will save the vtable pointer in each instance. From the two reasons, the first being the important one.
struct base1 {};
struct base2 {
virtual ~base2() {}
};
struct base3 {
protected:
~base3() {}
};
typedef base1 base;
struct derived : base { int x; };
struct other { int y; };
int main() {
std::auto_ptr<derived> d( new derived() ); // ok: deleting at the right level
std::auto_ptr<base> b( new derived() ); // error: deleting through a base
// pointer with non-virtual destructor
}
The problem in the last line of main can be resolved in two different ways. If the typedef is changed to base1 then the destructor will correctly be dispatched to the derived object and the code will not cause undefined behavior. The cost is that derived now requires a virtual table and each instance requires a pointer. More importantly, derived is no longer layout compatible with other. The other solution is changing the typedef to base3, in which case the problem is solved by having the compiler yell at that line. The shortcoming is that you cannot delete through pointers to base, the advantage is that the compiler can statically ensure that there will be no undefined behavior.
In the particular case of the CRTP pattern (excuse the redundant pattern), most authors do not even care to make the destructor protected, as the intention is not to hold objects of the derived type by references to the base (templated) type. To be in the safe side, they should mark the destructor as protected, but that is rarely an issue.
Very unlikely indeed. There's nothing in the standard to stop compilers doing whole classes of stupidly inefficient things, but a non-virtual call is still a non-virtual call, regardless of whether the class has virtual functions too. It has to call the version of the function corresponding to the static type, not the dynamic type:
struct Foo {
void foo() { std::cout << "Foo\n"; }
virtual void virtfoo() { std::cout << "Foo\n"; }
};
struct Bar : public Foo {
void foo() { std::cout << "Bar\n"; }
void virtfoo() { std::cout << "Bar\n"; }
};
int main() {
Bar b;
Foo *pf = &b; // static type of *pf is Foo, dynamic type is Bar
pf->foo(); // MUST print "Foo"
pf->virtfoo(); // MUST print "Bar"
}
So there's absolutely no need for the implementation to put non-virtual functions in the vtable, and indeed in the vtable for Bar you'd need two different slots in this example for Foo::foo() and Bar::foo(). That means it would be a special-case use of the vtable even if the implementation wanted to do it. In practice it doesn't want to do it, it wouldn't make sense to do it, don't worry about it.
CRTP base classes really ought to have destructors that are non-virtual and protected.
A virtual destructor is required if the user of the class might take a pointer to the object, cast it to the base class pointer type, then delete it. A virtual destructor means this will work. A protected destructor in the base class stops them trying it (the delete won't compile since there's no accessible destructor). So either one of virtual or protected solves the problem of the user accidentally provoking undefined behavior.
See guideline #4 here, and note that "recently" in this article means nearly 10 years ago:
http://www.gotw.ca/publications/mill18.htm
No user will create a Base<Derived> object of their own, that isn't a Derived object, since that's not what the CRTP base class is for. They just don't need to be able to access the destructor - so you can leave it out of the public interface, or to save a line of code you can leave it public and rely on the user not doing something silly.
The reason it's undesirable for it to be virtual, given that it doesn't need to be, is just that there's no point giving a class virtual functions if it doesn't need them. Some day it might cost something, in terms of object size, code complexity or even (unlikely) speed, so it's a premature pessimization to make things virtual always. The preferred approach among the kind of C++ programmer who uses CRTP, is to be absolutely clear what classes are for, whether they are designed to be base classes at all, and if so whether they are designed to be used as polymorphic bases. CRTP base classes aren't.
The reason that the user has no business casting to the CRTP base class, even if it's public, is that it doesn't really provide a "better" interface. The CRTP base class depends on the derived class, so it's not as if you're switching to a more general interface if you cast Derived* to Base<Derived>*. No other class will ever have Base<Derived> as a base class, unless it also has Derived as a base class. It's just not useful as a polymorphic base, so don't make it one.
The answer to your first question: No. Only calls to virtual functions will cause an indirection via the virtual table at runtime.
The answer to your second question: The Curiously recurring template pattern is commonly implemented using private inheritance. You don't model an 'IS-A' relationship and hence you don't pass around pointers to the base class.
For instance, in
template <class Derived> class Base
{
};
class Derived : Base<Derived>
{
};
You don't have code which takes a Base<Derived>* and then goes on to call delete on it. So you never attempt to delete an object of a derived class through a pointer to the base class. Hence, the destructor doesn't need to be virtual.
Firstly, I think the answer to the OP's question has been answered quite well - that's a solid NO.
But, is it just me going insane or is something going seriously wrong in the community? I felt a bit scared to see so many people suggesting that it's useless/rare to hold a pointer/reference to Base. Some of the popular answers above suggest that we don't model IS-A relationship with CRTP, and I completely disagree with those opinions.
It's widely known that there's no such thing as interface in C++. So to write testable/mockable code, a lot of people use ABC as an "interface". For example, you have a function void MyFunc(Base* ptr) and you can use it this way: MyFunc(ptr_derived). This is the conventional way to model IS-A relationship which requires vtable lookups when you call any virtual functions in MyFunc. So this is pattern one to model IS-A relationship.
In some domain where performance is critical, there exists another way(pattern two) to model IS-A relationship in a testable/mockable manner - via CRTP. And really, performance boost can be impressive(600% in the article) in some cases, see this link. So MyFunc will look like this template<typename Derived> void MyFunc(Base<Derived> *ptr). When you use MyFunc, you do MyFunc(ptr_derived); The compiler is going to generate a copy of code for MyFunc() that matches best with the parameter type ptr_derived - MyFunc(Base<Derived> *ptr). Inside MyFunc, we may well assume some function defined by the interface is called, and pointers are statically cast-ed at compile time(check out the impl() function in the link), there's no overheads for vtable lookups.
Now, can someone please tell me either I am talking insane nonsense or the answers above simply did not consider the second pattern to model IS-A relationship with CRTP?
When exactly does the compiler create a virtual function table?
1) when the class contains at least one virtual function.
OR
2) when the immediate base class contains at least one virtual function.
OR
3) when any parent class at any level of the hierarchy contains at least one virtual function.
A related question to this:
Is it possible to give up dynamic dispatch in a C++ hierarchy?
e.g. consider the following example.
#include <iostream>
using namespace std;
class A {
public:
virtual void f();
};
class B: public A {
public:
void f();
};
class C: public B {
public:
void f();
};
Which classes will contain a V-Table?
Since B does not declare f() as virtual, does class C get dynamic polymorphism?
Beyond "vtables are implementation-specific" (which they are), if a vtable is used: there will be unique vtables for each of your classes. Even though B::f and C::f are not declared virtual, because there is a matching signature on a virtual method from a base class (A in your code), B::f and C::f are both implicitly virtual. Because each class has at least one unique virtual method (B::f overrides A::f for B instances and C::f similarly for C instances), you need three vtables.
You generally shouldn't worry about such details. What matters is whether you have virtual dispatch or not. You don't have to use virtual dispatch, by explicitly specifying which function to call, but this is generally only useful when implementing a virtual method (such as to call the base's method). Example:
struct B {
virtual void f() {}
virtual void g() {}
};
struct D : B {
virtual void f() { // would be implicitly virtual even if not declared virtual
B::f();
// do D-specific stuff
}
virtual void g() {}
};
int main() {
{
B b; b.g(); b.B::g(); // both call B::g
}
{
D d;
B& b = d;
b.g(); // calls D::g
b.B::g(); // calls B::g
b.D::g(); // not allowed
d.D::g(); // calls D::g
void (B::*p)() = &B::g;
(b.*p)(); // calls D::g
// calls through a function pointer always use virtual dispatch
// (if the pointed-to function is virtual)
}
return 0;
}
Some concrete rules that may help; but don't quote me on these, I've likely missed some edge cases:
If a class has virtual methods or virtual bases, even if inherited, then instances must have a vtable pointer.
If a class declares non-inherited virtual methods (such as when it doesn't have a base class), then it must have its own vtable.
If a class has a different set of overriding methods than its first base class, then it must have its own vtable, and cannot reuse the base's. (Destructors commonly require this.)
If a class has multiple base classes, with the second or later base having virtual methods:
If no earlier bases have virtual methods and the Empty Base Optimization was applied to all earlier bases, then treat this base as the first base class.
Otherwise, the class must have its own vtable.
If a class has any virtual base classes, it must have its own vtable.
Remember that a vtable is similar to a static data member of a class, and instances have only pointers to these.
Also see the comprehensive article C++: Under the Hood (March 1994) by Jan Gray. (Try Google if that link dies.)
Example of reusing a vtable:
struct B {
virtual void f();
};
struct D : B {
// does not override B::f
// does not have other virtuals of its own
void g(); // still might have its own non-virtuals
int n; // and data members
};
In particular, notice B's dtor isn't virtual (and this is likely a mistake in real code), but in this example, D instances will point to the same vtable as B instances.
The answer is, 'it depends'. It depends on what you mean by 'contain a vtbl' and it depends on the decisions made by the implementor of the particular compiler.
Strictly speaking, no 'class' ever contains a virtual function table. Some instances of some classes contain pointers to virtual function tables. However, that's just one possible implementation of the semantics.
In the extreme, a compiler could hypothetically put a unique number into the instance that indexed into a data structure used for selecting the appropriate virtual function instance.
If you ask, 'What does GCC do?' or 'What does Visual C++ do?' then you could get a concrete answer.
#Hassan Syed's answer is probably closer to what you were asking about, but it is really important to keep the concepts straight here.
There is behavior (dynamic dispatch based on what class was new'ed) and there's implementation. Your question used implementation terminology, though I suspect you were looking for a behavioral answer.
The behavioral answer is this: any class that declares or inherits a virtual function will exhibit dynamic behavior on calls to that function. Any class that does not, will not.
Implementation-wise, the compiler is allowed to do whatever it wants to accomplish that result.
Answer
a vtable is created when a class declaration contains a virtual function. A vtable is introduced when a parent -- anywhere in the heirarchy -- has a virtual function, lets call this parent Y. Any parent of Y WILL NOT have a vtable (unless they have a virtual for some other function in their heirarchy).
Read on for discussion and tests
-- explanation --
When you specify a member function as virtual, there is a chance that you may try to use sub-classes via a base-class polymorphically at run-time. To maintain c++'s guarantee of performance over language design they offered the lightest possible implementation strategy -- i.e., one level of indirection, and only when a class might be used polymorphically at runtime, and the programmer specifies this by setting at least one function to be virtual.
You do not incur the cost of the vtable if you avoid the virtual keyword.
-- edit : to reflect your edit --
Only when a base class contains a virtual function do any other sub-classes contain a vtable. The parents of said base class do not have a vtable.
In your example all three classes will have a vtable, this is because you can try to use all three classes via an A*.
--test - GCC 4+ --
#include <iostream>
class test_base
{
public:
void x(){std::cout << "test_base" << "\n"; };
};
class test_sub : public test_base
{
public:
virtual void x(){std::cout << "test_sub" << "\n"; } ;
};
class test_subby : public test_sub
{
public:
void x() { std::cout << "test_subby" << "\n"; }
};
int main()
{
test_sub sub;
test_base base;
test_subby subby;
test_sub * psub;
test_base *pbase;
test_subby * psubby;
pbase = ⊂
pbase->x();
psub = &subby;
psub->x();
return 0;
}
output
test_base
test_subby
test_base does not have a virtual table therefore anything casted to it will use the x() from test_base. test_sub on the other hand changes the nature of x() and its pointer will indirect through a vtable, and this is shown by test_subby's x() being executed.
So, a vtable is only introduced in the hierarchy when the keyword virtual is used. Older ancestors do not have a vtable, and if a downcast occurs it will be hardwired to the ancestors functions.
You made an effort to make your question very clear and precise, but there's still a bit of information missing. You probably know, that in implementations that use V-Table, the table itself is normally an independent data structure, stored outside the polymorphic objects, while objects themselves only store a implicit pointer to the table. So, what is it you are asking about? Could be:
When does an object get an implicit pointer to V-Table inserted into it?
or
When is a dedicated, individual V-Table created for a given type in the hierarchy?
The answer to the first question is: an object gets an implicit pointer to V-Table inserted into it when the object is of polymorphic class type. The class type is polymorphic if it contains at least one virtual function, or any of its direct or indirect parents are polymorphic (this is answer 3 from your set). Note also, that in case of multiple inheritance, an object might (and will) end up containing multiple V-Table pointers embedded into it.
The answer to the second question could be the same as to the first (option 3), with a possible exception. If some polymorphic class in single inheritance hierarchy has no virtual functions of its own (no new virtual functions, no overrides for parent virtual function), it is possible that implementation might decide not to create an individual V-Table for this class, but instead use it's immediate parent's V-Table for this class as well (since it is going to be the same anyway). I.e. in this case both objects of parent type and objects of derived type will store the same value in their embedded V-Table pointers. This is, of course, highly dependent on implementation. I checked GCC and MS VS 2005 and they don't act that way. They both do create an individual V-Table for the derived class in this situation, but I seem to recall hearing about implementations that don't.
C++ standards doesn't mandate using V-Tables to create the illusion of polymorphic classes. Most of the time implementations use V-Tables, to store the extra information needed. In short, these extra pieces of information are equipped when you have at least one virtual function.
The behavior is defined in chapter 10.3, paragraph 2 of the C++ language specification:
If a virtual member function vf is
declared in a class Base and in a
class Derived, derived directly or
indirectly from Base, a member
function vf with the same name and
same parameter list as Base::vf is
declared, then Derived::vf is also
virtual ( whether or not it is so
declared ) and it overrides Base::vf.
A italicized the relevant phrase. Thus, if your compiler creates v-tables in the usual sense then all classes will have a v-table since all their f() methods are virtual.