Doc. No.: N2965=09-0155
Date: 2009-09-25
Author: Michael Spertus
Email: mike_spertus@symantec.com

Type traits and base classes

This paper describes a very simple and powerful type trait that enumerates the base classes of a class

The rationale for this can be summed up quickly.

The bases trait

This section informally describes the std::bases type trait that gives the base classes of a class. Precise definitions are given in the Wording section.

bases<C>::type is the type tuple<all base classes of C>.
direct_bases<C>::type is the type tuple<all direct base classes of C>.

Notes:

Simple example

This simple examples illustrates the results of these type traits. In the Suppose we have the following class hierarchy:
class E {};
class D {};
class C : virtual public D, private E {};
class B : virtual public D, public E {};
class A : public B, public C {};
It follows that bases<A>::type is tuple<D, B, E, C, E>

Similarly, direct_bases<A>::type is tuple<B, C>

Motivating examples

Writing (roughly) parallel type hierarchies is central to many programming best practices. The problem with parallel hierarchies is that they require a lot of tedious boilerplate to write and keeping these hierarchies in sync as the interface hierarchies evolve is generally an ongoing maintenance nightmare.

Indeed, code generation tools like Java's EMF (Eclipse Modeling Framework) automatically generate classes in three parallel class hierarchies for each UML class; An interface class; an implementation class; and a presentation class (referred to as the provider class). Any time a change is made to the inheritance hierarchy, all the code needs to be regenerated, creating all kinds of merge problems with any modifications to the previously generated code skeleton (I am experiencing this repeatedly in an EMF project I am involved with).

In the code below, we suppose that we have a hierarchy of interface classes that only contain abstract methods and inherit virtually (i.e., like Java and C# interfaces).

The following code can go in an impl.h header that automatically generates implementation classes from an interface and its superclasses.
impl.h
template<class T> class impl_base;
template<class T> class impl_helper : virtual public T, public impl_base<typename direct_bases<T>::type> {};
template<class T> class impl : public impl_helper<T> {};
template<> class impl_base<tuple<>> {};

template<class T, typename... remainder>
class impl_base<T, remainder...>> 
  : virtual public impl<T>, public impl_base<tuple<remainder...>> {  
};
Now, with no additional code whatsoever (beyond #include "impl.h"), impl<PushButton> automatically is defined and inherits from impl<Button> (as long as PushButton inherits from Button). If impl<PushButton> can inherit all of its functionality from impl<Button>, we are done. If not, adding custom functionality to the implementation classes is almost as easy:

#include "impl.h"
template<>
impl<PushPutton> : public impl_helper<PushButton>
{
  void buttonPushed() { ... } // Implement PushButton::buttonPushed
};
and your own class is automatically placed where it belongs in the inheritance hierarchy.

Note that programmers that use impl.h don't need to understand type traits or its internals, any more than users of STL need to understand the STL sources.

We can build on this example to create an impl_family.h header that can automatically generate implementation families parallel to a given interface hierarchy, such as Qt<PushButton> and Gtk<PushButton>, etc.
impl_family.h
template<template <typename> class Family, class T> class impl_family_base;
template<template <typename> class Family, class T>
class impl_family_helper : virtual public T, public impl_family_base<Family, typename direct_bases<T>::type>{};

template<template <typename> class Family, class T> class impl_family : public impl_family_helper<Family, T> {};
template<template<typename> class Family> class impl_family_base<Family, tuple<>> {};

template<template<typename> class Family, class T, typename... remainder>
class impl_family_base<Family, tuple<T, remainder...>> 
  : virtual public Family<T>, public impl_family_base<Family,tuple<remainder...>> {};
Note: impl.h and impl_family.h have been confirmed to run well on g++ 4.3.3 (assuming the presence of the direct_bases template).

We have programs used at my company where this would automatically eliminate thousands of lines of intricate and difficult to maintain boilerplate template code.

It is our hope that examples such as these will convince the reader that inquiring about base classes are sufficiently fundamental to "type traits" that there will be many good uses (even beyond building parallel hierarchies).

Wording

In Section 20.6.2 (Header <type_traits> synopsis), insert where indicated between the gray context lines,

    template <class... T> struct common_type;
    template<class T>struct bases;  
    template<class T>struct direct_bases;  
  } // namespace std
Add the following two rows to the end the table in 20.6.7 (Other transformations), :
template <class T> struct bases If T is a class, the member typedef type shall be a tuple whose types are all base classes of T in the order specified for initializing bases in 20.6.2. If T is not a class, then the member typedef type shall be a tuple with zero arguments
template <class T> struct direct_bases If T is a class, the member typedef type shall be a tuple whose types are all direct base classes of T in the order specified for initializing bases in 20.6.2. If T is not a class, then the member typedef type shall be a tuple with zero arguments

Add the following to the end of 20.6.7:

[Example

class E {};
class D {};
class C : virtual public D, private E {};
class B : virtual public D, public E {};
class A : public B, public C {};
// the following assertions hold:
assert((is_same<bases<A>::type, tuple<D, B, E, C, E>>::value));
assert((is_same<direct_bases<A>::type, tuple<B, C>>::value));
end example]

Appendix: Future enhancements

Obviously, the traits described above offer a very simplified view of the type system (e.g., not recognizing virtual inheritance or accessibility constraints). Eventually, we would like a more full-featured system for metaprogramming the type system. As it is always good to have a vision, this section describes one possible thought as to how this may evolve and what a future system for modeling base class relationships (maybe even broader reflection) could look like.

The important thing to note is that there is no harm and much advantage in implementing the traits above at the earliest opportunity, even if we add a more full-featured framework later. The main point is that the bases and direct_bases are fully consistent with the approach currently taken by is_base_of to model base class relations. If we have is_base_of, we might as well have bases, as it gives us much more power without being committal about the future. Indeed, the sketch below also includes a base_occurrences type trait that adds all the same detail to is_base_of. In the author's opinion, there is no reason to hold back on adding bases and direct_bases at soon as possible because of fear that something better (perhaps different than described below) could be implemented down the road.

The describe_bases trait

This section informally describes the std::describe_bases type trait that gives a detailed description of the base classes of a class.

Since C++ supports many types of base classes, as a preliminary to describing the bases type trait, we define a base_info class whose template parameters describe the relationship of a class to its parent to allow accurate reconstruction of the inheritance hierarchy.

namespace std {
  enum member_accessibility {
    public_accessibility,
    protected_accessibility,
    private_accessibility,
    inaccessible
  };

  template<class baseT, bool direct, bool virt, member_accessibility access> 
  class base_info {
  public:
    typedef baseT base;  
    const static bool is_direct = direct;
    const static bool is_virtual = virt;
    const static member_accessibility accessibility = access;
  };
}
Now we are ready to describe the describe_bases type trait.

describe_bases<C>::type is the type tuple<base_info for all base classes of C>.

Base classes are listed in the order in which they are initialized (12.6.2p10).

Helper traits

While the bases trait gives us what we absolutely need for walking the type hierarchy, the following "helper" type traits are "very nice to have," avoiding compile-time and space-intensive metaprograms for common scenarios.

Since the inheritance hierarchy is generally understood as a tree, an easy way to enumerate the direct base classes of a class is generally what we need to walk a class' inheritance hierarchy:

describe_direct_bases<C>::type is the type tuple<base_info for all direct base classes of C>.

The existing is_base_of type trait says that a class is a base class of another, but does not say anything about what kind of base class. We can extend the is_base_of<B, D> with the following type trait:

base_occurrences<B, D> is tuple<base_info for all occurrences of B as a base class of D>.

Simple example

This simple examples illustrates the results of these type traits. In the Suppose we have the following class hierarchy:
class E {};
class D {};
class C : virtual public D, private E {};
class B : virtual public D, public E {};
class A : public B, public C {};
It follows that describe_bases<A>::type is
tuple<base_info<D, false, true, public_accessibility>,
      base_info<B, true, false, public_accessibility>,
      base_info<E, false, false, public_accessibility>,
      base_info<C, true, false, public_accessibility>,
      base_info<E, false, false, inaccessible>> // Not accessible in A


and that describe_direct_bases<A>::type is

tuple<base_info<B, true, false, public_accessibility>,
      base_info<C, true, false, public_accessibility>>


We also have that base_occurrences<E, A>::type is

tuple<base_info<E, false, false, public_accessibility>,
      base_info<E, false, false, inaccessible>>


Of course, base_occurrences<B, C>::type is just tuple<>.

Extended example

The following code shows how to produce a file equivalent to the impl.h header described in the Motivating examples above.

template<class T> class impl_base;
template<class T> class impl_helper : virtual public T, public impl_base<typename describe_direct_bases<T>::type> {};
template<class T> class impl : public impl_helper<T> {};
template<>class impl_base<tuple<>> {};

template<class T, typename... remainder>
class impl_base<tuple<base_info<T, true, true, public_accessibility >,remainder...>> 
  : virtual public impl<T>, public impl_base<tuple<remainder...>> {  
};
This acts like the previous impl.h. However, it produces a compiler diagnostic if the assumption of virtual inheritance is among the interfaces is violated. It also can be fine tuned to handle different base class accessibilities as appropriate, unlike the previous one, which inappropriately propagates private base classes to the implementation hierarchy.

Note:This impl.h has also been confirmed to run perfectly on g++ 4.3.3 (assuming the presence of the describe_direct_bases template).