Concept based overloading on array views in C++03

Back to programming

Introduction

The next C++09 standard will introduce concepts and concept based overloading to C++. However, is there any way to simulate concept based overloading in C++03 already? To answer this question partially, we give an example that mimics concept based overloading on array views. Concept checking can be achieved with Boost's concept checking library, but that is not dealt with in this article.

Setting up the situation

Assume we have the following set of concepts:

The idea is that the types that model the Array2Concept contain all the data. As an example we could have a class template 'LinearArray2<Type>' storing elements of type 'Type' in a familiar row-major memory order. Another class template 'BlockArray2<Type>' could store the data in blocks to enhance cache behaviour in graphics applications.

While Array2:s store the data, we wish to access the data through views. An example view would be a class template 'Array2View2<Array2>' which takes a reference to an Array2 and offers access to its elements (more precisely, an Array2 offers cursors/iterators to its elements which the views propagate or possibly modify). Then again, we could have another class template 'SubView2<View2>' which can be used to select a sub-view from a view, or a 'TransposeView2<View2>' which offers a transposed view of a view. All views also have const counterparts, which means that they are read-only. If a view is View2, then it is also ConstView2.

Problem

We wish to code a function that copies the values of one view to another view. This is how we might approach the problem:

template <typename Input_ConstView2, typename Output_View2>
void copy(
    const Input_ConstView2& input,
    const Output_View2& output)
{
    // Copy..
}

However, the problematic thing about this function is that it matches a lot of other things too, types that do not model View2Concept at all. The problem is of course that the template function is unrestricted. To make the unintentional matching less probable we might prefix the name 'copy' to become 'viewCopy'. However, that seems a bit redundant. After all, we know that we are holding a pair of views in our hands. What should we do then?

Concept wrapping

Our strategy is to wrap all types that model the View2Concept (or a ConstView2Concept) into a View2 (or a ConstView2) class that itself models the View2Concept (or the ConstView2Concept).

template <typename Type, typename Contained_ConstView2>
class ConstView2
{
public:
    typedef typename Contained_ConstView2::ConstCursor ConstCursor;
    typedef typename Contained_ConstView2::Element Element;

    explicit ConstView2(const Contained_ConstView2& view)
        : view_(view)
    {
    }

    integer width() const
    {
        return view_.width();
    }

    integer height() const
    {
        return view_.height();
    }

    ConstCursor constCursor(integer x, integer y) const
    {
        return view_.constCursor(x, y);
    }

protected:
    Contained_ConstView2 view_;
};

template <typename Contained_ConstView2>
ConstView2<typename Contained_ConstView2::Element, Contained_ConstView2> 
    constView2(const Contained_ConstView2& view)
{
    return ConstView2<typename Contained_ConstView2::Element, Contained_ConstView2>(view);
}

template <typename Type, typename Contained_View2>
class View2
    : public ConstView2<Type, Contained_View2>
{
public:
    typedef typename Contained_View2::Cursor Cursor;

    explicit View2(const Contained_View2& view)
        : ConstView2<Type, Contained_View2>(view)
    {
    }

    // Needed because base class is dependent on 
    // template parameters.
    using ConstView2<Type, Contained_View2>::view_;

    Cursor cursor(integer x, integer y) const
    {
        return view_.cursor(x, y);
    }
};

template <typename Contained_View2>
View2<typename Contained_View2::Element, Contained_View2> view2(
    const Contained_View2& view)
{
    return View2<typename Contained_View2::Element, Contained_View2>(view);
}

Now, whenever we implement a function, we code for the View2 and ConstView2 class templates, not for the actual type:

template <
    typename Input_Element, 
    typename Input_ConstView2,
    typename Output_Element,
    typename Output_View2>
void copy(
    const ConstView2<Input_Element, Input_ConstView2>& input,
    const View2<Output_Element, Output_View2>& output)
{
    // Copy...
}

We have just emulated concept based overloading! But not just that. We have also extracted associated types as part of the class template parameters, which is quite handy when you wish to specify restrictions on them or implement a specialization for some type. For example, suppose we would like to require that Input_Element is the same type as Output_Element. This is done simply by:

template <
    typename Element,
    typename Input_ConstView2,
    typename Output_View2>
void copy(
    const ConstView2<Element, Input_ConstView2>& input,
    const View2<Element, Output_View2>& output)
{
    // Copy...
}

When we implement the views, we wrap them up like this:

template <typename Contained_View2>
class TransposedView2
{
// ...
};

template <typename Contained_View2>
View2<typename Contained_View2::Element, TransposedView2<Contained_View2> > 
    transposedView2(const Contained_View2& view)
{
    return view2(TransposedView2<Contained_View2>(view));
}

// Same things for ConstTransposedView2 follow..

And here's an example what you can achieve with such a system:

LinearArray2<float> aImage;
BlockArray2<float> bImage;

// Fill aImage and bImage with some data...

copy(constSubView2(constArray2View2(aImage), Rectangle(0, 0, 100, 100)), 
    subView2(transposedView2(array2View2(bImage)), Rectangle(100, 100, 200, 200)));

Caveats

Note that the contained views are stored by value in the View2 class. In our case the views are very light so this is not a problem. But clearly this is not practical for larger objects, which limits the usage of this technique.

Summary

We discussed a wrapping technique that mimics concept based overloading on array views. In addition it can extract associated types to template parameters.