» Mapping arrays using tuples in C++11

July 11, 2014 | cpp development english | Adrian Kummerländer

During my proof-of-concept implementation of external functions enabling XSLT based static site generation I came upon the problem of calling a template method specialized on the Nth type of a std::tuple specialization using the Nth element of a array-like type instance as input. This was needed to implement a generic template-based interface for implementing Apache Xalan external functions. This article aims to explain the particular approach taken to solve this problem.

While it is possible to unpack a std::tuple instance into individual predefined objects using std::tie the standard library offers no such helper template for unpacking an array into individual objects and calling appropriate casting methods defined by a std::tuple mapping type. Sadly exactly this functionality is needed so that we are able to call a constructDocument member method of a class derived from the FunctionBase external function interface template class using static polymorphism provided through the curiously recurring template pattern. This interface template accepts the desired external function arguments as variadic template types and aims to provide the required validation and conversion boilerplate implementation. While we could recursively generate a std::tuple specialization instance from an array-like type using a approach simmilar to the one detailed in my article on mapping binary structures as tuples using template metaprogramming this wouldn't solve the problem of passing on the resulting values as individual objects. This is why I had to take the new approach of directly calling a template method on individual array elements using a std::tuple specialization as a kind of blueprint and passing the result values of this method to the constructDocument method as separate arguments.

Extracting array elements obviously requires some way of defining the appropriate indexes and mapping the elements using a tuple blueprint additionally requires this way to be statically resolvable as one can not pass a dynamic index value to std::tuple_element. So the first step to fullfilling the defined requirements involved the implementation of a template based index or sequence type.

template <std::size_t...>
struct Sequence {
    typedef Sequence type;
};

template <
    std::size_t Size,
    std::size_t Index = 0,
    std::size_t... Current
>
struct IndexSequence {
    typedef typename std::conditional<
        Index < Size,
        IndexSequence<Size, Index + 1, Current..., Index>,
        Sequence<Current...>
    >::type::type type;
};

This is achieved by the IndexSequence template above by recursively specializing the Sequence template using static recursion controlled by the standard library's template metaprogramming utility template std::conditional. This means that e.g. the type Sequence<0, 1, 2, 3> can also be defined as IndexSequence<4>::type.

Now all that is required to accomplish the goal is instantiating the sequence type and passing it to a variadic member template as follows:

[...]
this->callConstructDocument(
    parameters,
    locator,
    typename IndexSequence<parameter_count>::type()
)
[...]
template <std::size_t... Index>
inline xalan::XalanDocument* callConstructDocument(
    const XObjectArgVectorType& parameters,
    const xalan::Locator* locator,
    Sequence<Index...>
) const {
    [...]
    return this->document_cache_->create(
        static_cast<const Implementation*>(this)->constructDocument(
            valueGetter.get<typename std::tuple_element<
                Index,
                std::tuple<Types...>
            >::type>(parameters[Index])...
        )
    );
}

As we can see a IndexSequence template specialization instance is passed to the variadic callConstructDocument method to expose the actual sequence values as Index. This method then resolves the Index parameter pack as both the array and std::tuple index inside the calls to the valueGetter.get template method which is called for every sequence element because of this. What this means is that we are now able to implement non-template constructDocument methods inside XSLT external function implementations such as FunctionTransform. The values passed to these methods are automatically extracted from the argument array and converted into their respective types as required.

While this article only provided a short overview of mapping arrays using tuples in C++11 one may view the full implementation on Github or cgit.

Update: The recently passed C++14 standard adds a std::integer_sequence template to the standard library which covers the same use case as the custom Sequence and IndexSequence templates detailed in this article. FunctionBase was already modified accordingly as one should obviously rely on the standard's version of a integer sequence in the future.