Search code examples
c++tuplesc++14template-meta-programmingtypename

Implementing map() and each() over std::tuple<...> - with index passed to functor as template parameter


After a few years of web development, I'm working in C++ (14) again and decided to have some "dynamically typed functional fun" with template metaprogramming.

I have implemented map and each over tuples:

template <typename Tuple, typename Func, size_t... index>
void tuple_each_internal(Tuple const & tuple, Func func, index_sequence<index...>)
{
    auto res = {
        (func(get<index>(tuple)), nullptr)...
    };
}

template <typename Tuple, typename Func, typename Indices = make_index_sequence<tuple_size<Tuple>::value>>
void tuple_each(Tuple const & tuple, Func func)
{
    tuple_each_internal(tuple, func, Indices());
}

struct demo_functor_each {

    /* Case #1: "Each" callback */
    template <typename T>
    void operator ()(T&& t) { ; }

    /* Case #2: "Each with index as run-time parameter" callback */
    //template <typename T>
    //void operator ()(const size_t index, T&& t) { ; }

    /* Case #3: "Each with index as compile-time parameter" callback */
    //template <typename T, size_t index>
    //void operator ()(T&& t) { ; }

};

void Example_Usage()
{
    tuple<int, bool, string> t;
    tuple_each(t, demo_functor_each());
}

And a similar implementation of map.

  • Case #1 passes syntax check (I haven't tried running it yet).

  • Case #2 also passes syntax check, with tuple_each_internal modified to pass the index as a function parameter: func(index, get<index>(tuple)).

  • Case #3 is superior to case #2 in that the value of index can be passed to other templates at compile time (e.g. get<index>(tuple)), which is not possible in case #2.

I have been unable to implement case #3.

Given the callback signature:

template <typename T, size_t index>
void operator ()(T&& t) { ; }

I tried this as tuple_each_internal:

auto res = {
    (func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)...
};

Clang++ 3.6.1 output:

$ clang++ -std=c++14 tuple_iteration.h

error: expected '(' for function-style cast or type construction

auto res = { (func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)... };
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

g++ 5.1.0 output:

$ g++ -std=c++14 tuple_iteration.h

error: expected ‘(’ before ‘,’ token

auto res = { (func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)... };
                                                             ^

Solution

  • It should be:

    auto res = {
        (func.template operator()<const typename tuple_element<index, Tuple>::type&, index>(get<index>(tuple)),
         nullptr)...
    };
    

    Live Demo.

    Simpler with

    /* Case #4: "Each with index as compile-time parameter" callback */
    template <size_t index, typename T>
    void operator ()(T&& t);
    

    where code becomes:

    auto res = {
        (func.template operator()<index>(get<index>(tuple)),
         nullptr)...
    };
    

    Live Demo

    or even

    template <size_t N, typename T>
    void operator ()(std::integral_constant<std::size_t, N>, T&& t);
    

    which results in

    auto res = {
        (func(std::integral_constant<std::size_t, index>{}, get<index>(tuple)),
         nullptr)...
    };
    

    Live Demo