Search code examples
c++code-duplication

Orthogonal variables code duplication problem


I've started refactoring some legacy code recently and came across two functions for drawing a coordinate grid, the problem is that these functions differ only in orthogonal variables they treat, something like that

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int x = x0; x < x1; x += step)
    {
         MoveToEx(dc, x, y0, NULL);
         LineTo(dc, x, y1);
    }
}
void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int y = y0; y < y1; y += step)
    {
         MoveToEx(dc, x0, y, NULL);
         LineTo(dc, x1, y);
    }
}

So if I decide to add some fancy stuff, like antialiasing or merely change drawing pencil or whatever I'll have to put the same code in both of them and it's code duplication and it's bad we all know why.

My question is how would you rewrite these two functions into a single one to avoid this problem?


Solution

  • Drawing a line is simply joining two points, and drawing a scaling incrementing (x0,y0) and(x1,y1) in a particular direction, through X, and/or through Y. This boils down to, in the scale case, which direction(s) stepping occurs (maybe both directions for fun).

    template< int XIncrement, YIncrement >
    struct DrawScale
    {
      void operator()(HDC dc, int step, int x0, int x1, int y0, int y1)
      {
        const int deltaX = XIncrement*step;
        const int deltaY = YIncrement*step;
        const int ymax = y1;
        const int xmax = x1;
        while( x0 < xmax && y0 < ymax )
        {
            MoveToEx(dc, x0, y0, NULL);
            LineTo(dc, x1, y1);
            x0 += deltaX;
            x1 += deltaX;
            y0 += deltaY;
            y1 += deltaY;
        }
      }
    };
    typedef DrawScale< 1, 0 > DrawScaleX;
    typedef DrawScale< 0, 1 > DrawScaleY;
    

    The template will do its job: at compile time the compiler will remove all the null statements i.e. deltaX or deltaY is 0 regarding which function is called and half of the code goes away in each functor.

    You can add you anti-alias, pencil stuff inside this uniq function and get the code properly generated generated by the compiler.

    This is cut and paste on steroids ;-)

    -- ppi