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.
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.
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();
}
};
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();
}
};
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; }
};