Mixins in straight C++

The technique of using mixins in straight C++ is illustrated for writing GUI widgets

For simplicity it is assumed the GUI elements appear in a rectangular region and the examples only show how mixins can be used to help write GetWidth() and GetHeight() methods. In a more complete example, methods to perform drawing (e.g. by issuing OpenGL commands), hit testing and to process mouse events would be defined as well.

Some example mixins

Rotate90

The mixin Rotate90 applies a rotation of 90 degrees to its base class. Note that when a rectangle is rotated by 90 degrees its width becomes its height and vice versa.


template <class Base>
struct Rotate90 : public Base
{
    int GetWidth() const
    {
        return Base::GetHeight();
    }
    int GetHeight() const
    {
        return Base::GetWidth();
    }
};

In a more complete example, methods would also be implemented to apply a rotation transformation before calling the base class draw method (this for example would simply involve a call to glRotate in OpenGL), and to rotate (x,y) positions passed into hit testing or mouse event methods. The upshot is that one can rotate any GUI control by 90 degrees, and it works exactly as expected. For example a horizontal slider control becomes a vertical slider, and even allows the mouse to be used to drag the slider thumb in a vertical direction.

Scale

The following mixin scales the x coordinate by scalex, and the y coordinate by scaley.


template <class Base, int scalex, int scaley>
struct Scale : public Base
{
    int GetWidth() const
    {
        return scalex * Base::GetWidth();
    }
    int GetHeight() const
    {
        return scaley * Base::GetHeight();
    }
};

Border

The following mixin applies a border (i.e. left, right, top and bottom margins) around its base class.


template <class Base, int border>
struct Border : public Base
{
    int GetWidth() const
    {
        return 2*border + Base::GetWidth();
    }
    int GetHeight() const
    {
        return 2*border + Base::GetHeight();
    }
};

Using the mixins

In order to use the mixins, a concrete class is needed that can be fed into the base of the mixin chain. For this purpose, consider a rather conventional C++ class named UnitSquare.


struct UnitSquare
{
    int GetWidth() const { return 1; }
    int GetHeight() const { return 1; }
};

Mixins are applied in a linear chain involving single inheritance. Interestingly the same mixin can usefully appear more than once in the chain. Most generally the order in which the mixins are applied is significant. For example applying a border before it is scaled, means that the margins are scaled as well.


struct X : Scale< Border< Rotate90< Scale<UnitSquare,2,1> >,1 >,10,3 >
{
};

It turns out that X::GetWidth() returns 30, and X::GetHeight() returns 12.

Modern C++ compilers are rather good at inlining, so typically the release build is exactly the same as if the following had been entered by the programmer:


struct X
{
    int GetWidth() const { return 30; }
    int GetHeight() const { return 12; }
};