1

In my previous question, it was highlight that implementations of C++ such as GCC must store a vtable pointer in every copy of a class for each parent class which has a virtual function.

So a class which implements say 5 "interfaces" (i.e. pure abstract classes) must be at least 5 pointers in size before even it's own members are added.

At first glance, this seems necessary. When we upcast, all we can do is change the pointer. But we need a new vtable for that upcasted pointer. So there's not much option other than putting it in the object itself.

The downside of this is that we pay a per-object cost for virtual parent classes, even ones with no members.

How about instead, making a pointer "two pointers". The first pointer is a pointer to the object. The second is a pointer to it's vtable. This could perhaps only be done for virtual objects.

The object itself could now just be pure data, no need for vtable pointers.

Note then we only need one vtable pointer, even if there's multiple parents. The vtable itself could have entries describing how to upcast, or perhaps the compiler just knows how to do this.

Is there any reason this approach is not viable> Are there any C++ implementations that do this?

(Note: I understand one can avoid pure abstract bases as interfaces and "duck typing" and in the future concepts but lets ignore that for the moment)

3
  • In practice it does not matter: multiple inheritance is rarely used, and in the few cases it is used the object is usually quite "big" -data fields are also inherited- (and the multiple superclasses are few, more often 2 than 5). Aug 6, 2018 at 13:36
  • If you have a good-enough reason, implementing some traits-type isn't too difficult. The lack of language-level-support makes it a lot more verbose though, no way around it. At least implicit traitt instantiations can easily be supported with templates. Oct 11, 2018 at 10:05
  • I wonder if this is related to the necessity of thunking - adjusting pointer address as part of down-casting to an interface (abstract base class) pointer, as well as before/after function calls? The need to have unique address makes it necessary to have at least that same number of vtable pointers.
    – rwong
    Oct 11, 2018 at 22:40

4 Answers 4

10

How about instead, making a pointer "two pointers".

This would bloat every pointer, even those which don't use dynamic polymorphism. Remember: you have to be able to cast any object pointer to void* and back to that type with valid behavior. That wouldn't be possible unless every pointer used your "two pointer" approach. Plus, the C++ standard does not permit the size of an object pointer to be determined by what type it points to.

This would also break ABI with C and every C API function ever written. C++ implementations like to be able to call C APIs.

C++ is a language that prefers to follow the "don't pay for what you don't use" rule. Users who use lots of multiple inheritance of virtual interfaces pay for it; users who don't use these things don't have to pay anything. That is as it should be.

4

How about instead, making a pointer "two pointers".

Yes, I think that could be done, theoretically — there are a lot of ways C++ could be implemented. These choices are all trade-offs of one thing for another; though, there are lots of considerations to be made in such an implementation.

C++ avoids search across multiple inheritance parent classes by capitalizing on the point of conversion (from the concrete class to the (abstract) base). Other languages choose instead to search for an interface in the vtable of the concrete class (and instead do nothing at point of conversion), but pretty sure C++ could do that, too, if they wanted to, don't think there's any strict rule as to how it must be implemented.  (FYI, Doing nothing at point of conversion tends to be somewhat friendlier to garbage collection, another reason this tends to be favored by C# and Java.)

However, C++ allows instance fields in (abstract) base classes, whereas C# and Java don't support instance fields for interfaces.  This makes things a bit trickier for C++, since it has to know about data slots in the object as well as function slots in the vtable.  Perhaps due to this, I think, C++'s usually settle on adjusting the object pointer at point of conversion, rather than searching for data slots and searching for virtual function slots.

Note that a single object implementing 5 different interfaces is, IMHO, overrated.  You might find composition better for many scenarios, meaning that you would create several cooperating classes, each class implementing only one interface.

(Also note that the overhead you're describing only happens on the second parent class, not the first, as the subclass and its first base can share a vtable pointer.)

In any case, the overhead is minimal unless you're going to create a huge amount of these objects.  Usually, objects created in great quantities can be optimized in various ways, such as the (albeit complex) flyweight pattern.

3

How about instead, making a pointer "two pointers". The first pointer is a pointer to the object. The second is a pointer to it's vtable.

This is almost exactly how Go implements polymorphism via their interface{} mechanics.

Is there any reason this approach is not viable> Are there any C++ implementations that do this?

There is a toy approach defined here which implements a similar interface (excuse the pun) however it only works on pointers. It would need minor changes to also support references.

Because of the lack of language support, while using the interfaces can be made easy, defining interfaces becomes much more verbose and repetitive.

To summarize the interface definition from the link:

struct Unknown;

class Person {
    template <class T>
    struct implemented_by {
        const char* (T::*name) ();
        void (T::*talk) ();
        static const implemented_by vtable;
    };

    const implemented_by<Unknown>* const vt;
    Unknown* const p;

  public:
    inline const char* name () { return (p->*(vt->name))(); }
    inline void talk () { return (p->*(vt->talk))(); }

    <class T>
    Person (T* x) :
      vt(reinterpret_cast<const implemented_by*>(&implemented_by<T>::vtable)),
      p(reinterpret_cast<Unknown*>(x)) {}
};

template <class T>
const Person::implemented_by<T> Person::implemented_by<T>::vtable = {
    &(T::name),
    &(T::talk)
};

Some of this could be cleaned up by pulling the static vtable into the constructor template, but overall it probably isn't the best approach for production code.

I'd love to see this cleaned up. I took a crack at it myself, to no avail, and haven't had the time to revisit the concept. I was hoping to automate the vtable and data members via some combination of CRTP and type traits.

0

You've either misunderstood the answer to your other question or not read Richard Hodges reply.

Each C++ class instance possesses one, and only one, vtable pointer.

There is one vtable for each class type, e.g. if C inherits from A and B, there is a specific vtable for that combination.

Google "Itanium C++ ABI" for more details.

1
  • 4
    You're talking about multiple levels of single inheritance, where you're absolutely right, but the OP is about multiple inheritance. If you implement 5 unrelated interfaces, you have 5 unrelated base class subobjects in your derived class object, and each of those subobjects needs to be a valid object with its own base class-compatible vtable pointer. They can't overlap or share a single vtable pointer because any code taking a pointer to the abstract base type must be able to use it as such.
    – Useless
    Oct 11, 2018 at 9:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.