Suggestion : virtual member data

Steve Horne stephenwantshornenospam100 at aol.com
Wed Sep 6 02:26:19 PDT 2006


This suggestion was prompted by an existing thread title, but probably
has nothing to do with that thread.

In C++ development, I often find myself needing 'virtual member
variables'. By this, I mean data members that would be stored in the
virtual table.

As an example of the principle...


abstract class c_Container
{
  protected:
    abstract int  m_Max_Size;
    abstract bool m_Keys_Unique;

    abstract offsetof int[] m_Keys;
    abstract offsetof int[] m_Data;

  public:
    final int Key (int p_Index)
    {
      return m_Keys [p_Index];
    }
}

class c_Specific_Container
{
  protected:
    final int m_Max_Size     = 16;
    final bool m_Keys_Unique = true;

    int[m_Max_Size] m_Keys_Storage;
    int[m_Max_Size] m_Data_Storage;

    final offsetof int[] m_Keys = m_Keys_Storage;
    final offsetof int[] m_Data = m_Data_Storage;
}


Two tricks are implied above...

1.  m_Max_Size and m_Keys_Unique are constants to be referenced by the
    base class, but values are only assigned by the derived class.

    These can, in principle, simply be stored in the virtual table.

2.  m_Keys and m_Data refer to instance data in the derived class. The
    specific locations in the instance cannot be known in the base
    class and cannot be pre-allocated since the sizes aren't known.

    However, offsets into the instance can be held in the virtual
    table. And with the offsets available through the virtual table,
    the compiler can generate simple code to implement the lookup so
    that m_Keys and m_Data can generally be used as if they were
    member data in the base class.

Basically, the point is to allow base classes to refer to things
more-or-less directly that will be defined in the derived class, and
additionally to ensure that the derived class really does define them
(or else it is abstract and cannot be instantiated).

The rationale for this is about avoiding inner-loop overheads in a
safe way. All of these things could be handled using virtual member
functions, but this requires access functions. These access functions
cannot be inlined when called from the base class since the compiler
cannot know which implementation to inline - the final version is
defined by the derived class.

For instance, consider the trivial getter function Key which might be
called repeatedly in an inner loop. Ideally, it should be an inlined 
piece of trivial code. Needing a call to the derived class (to find
the actual location of the array) could easily be an inapproriate
overhead. As written above, there is still a virtual table lookup
overhead (and a bit of pointer arithmetic) but no function call
overhead. It should inline, and the optimiser may even be able to move
the m_Keys dereferencing (virtual table lookup of the offset, and
applying the offset to the instance pointer) out of inner loops.

This is enough of an issue that I have several examples in C++ where,
in effect, I've had to manage my own separate virtual tables for class
heirarchies. This can be very error prone, to say the least.

Abstract virtual members would not have values (as above) but there
may be some justification to having virtual member data that has a
default value, overrideable by a derived class. The above container
class might want to define a default maximum size, for instance.
Classes that included non-final definitions would not be able to
reference them at compile time, and would see the final overridden
definition at run-time.

The biggest issue would be how to declare overridable values,
considering that D takes the view that all member functions are
virtual, so the obvious keyword for C++ doesn't apply in D. Perhaps a
'vtable' storage class keyword?

As an aside, variables (as opposed to constants) held in the virtual
table might have some use, but the only things I can think of relate
to debug code and metrics. Getter and setter call overheads are mostly
irrelevant in those cases. I suspect the only kinds of 'variables'
that would be sensibly held in the virtual table are the (constant)
offsetof references to normal member data in the instance, as used for
the arrays in the above example.

Any thoughts?




More information about the Digitalmars-d mailing list