Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use two fold expressions in one line

Tags:

I am learning C++ fold expressions and they are really nice to expand one parameters pack. But what if I have to expand a few of them in one call?

I have prepared one example to clarify what I mean:

#include <cstddef>
#include <iostream>
#include <utility>

template<
    template<size_t ix_, size_t iy_> typename Functor,
    typename FunctorContext,
    size_t... ix,
    size_t... iy>
void RepeatImpl(
    FunctorContext* context,
    std::index_sequence<ix...>,
    std::index_sequence<iy...>)
{
    (Functor<ix, iy>::Execute(context), ...);
}

template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t width, size_t height,
    typename FunctorContext>
void Repeat(FunctorContext* context)
{
    RepeatImpl<Functor>(
        context,
        std::make_index_sequence<width>(),
        std::make_index_sequence<height>());
}

template<size_t ix, size_t iy>
struct Init2dArrayFunctor
{
    template<typename T>
    static void Execute(T* array)
    {
        array[iy][ix] = 10;
    }
};

int main(int, const char**)
{
    constexpr size_t w = 10;
    constexpr size_t h = 10;
    int array[w][h] { 0 };
    Repeat<Init2dArrayFunctor, w, h>(array);

    for(size_t iy = 0; iy < h; ++iy)
    {
        for(size_t ix = 0; ix < w; ++ix)
        {
            std::cout << array[iy][ix] << ' ';
        }

        std::cout << std::endl;
    }

    return 0;
}

My question is about this line:

(Functor<ix, iy>::Execute(context), ...);

In this particular example it will expanded to these calls:

Functor<0, 0>::Execute(context)
Functor<1, 1>::Execute(context)
Functor<2, 2>::Execute(context)
...

But I need it to call all combinations of ix and iy packs:

Functor<0, 0>::Execute(context)
Functor<0, 1>::Execute(context)
Functor<0, 2>::Execute(context)
...
Functor<1, 0>::Execute(context)
Functor<1, 1>::Execute(context)
Functor<1, 2>::Execute(context)
...

I know that I can do an additional layer to walk through rows and columns (two fold expressions in separate methods then):

#include <cstddef>
#include <iostream>
#include <utility>


template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t iRow,
    typename FunctorContext,
    size_t... iColumn>
void RepeatImpl_Row(
    FunctorContext* context,
    std::index_sequence<iColumn...>)
{
    (Functor<iColumn, iRow>::Execute(context), ...);
}

template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t columns,
    typename FunctorContext,
    size_t... iRow>
void RepeatImpl(
    FunctorContext* context,
    std::index_sequence<iRow...>)
{
    (RepeatImpl_Row<Functor, iRow>(context, std::make_index_sequence<columns>()), ...);
}

template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t width, size_t height,
    typename FunctorContext>
void Repeat(FunctorContext* context)
{
    RepeatImpl<Functor, width>(
        context,
        std::make_index_sequence<height>());
}

template<size_t ix, size_t iy>
struct Init2dArrayFunctor
{
    template<typename T>
    static void Execute(T* array)
    {
        array[iy][ix] = 10;
    }
};

int main(int, const char**)
{
    constexpr size_t w = 10;
    constexpr size_t h = 10;
    int array[w][h] { 0 };
    Repeat<Init2dArrayFunctor, w, h>(array);

    for(size_t iy = 0; iy < h; ++iy)
    {
        for(size_t ix = 0; ix < w; ++ix)
        {
            std::cout << array[iy][ix] << ' ';
        }

        std::cout << std::endl;
    }

    return 0;
}

But this way makes code much harder to read. Maybe is it possible to do this trick with two fold expressions in one line?

like image 595
Konstantin Lazukin Avatar asked Nov 29 '17 09:11

Konstantin Lazukin


1 Answers

you may simply write something like:

template<
    size_t height,
    template<size_t ix_, size_t iy_> typename Functor,
    typename FunctorContext,
    size_t... ixy>
void RepeatImpl(
    FunctorContext* context,
    std::index_sequence<ixy...>)
{
    (Functor< ixy / height, ixy % height >::Execute(context), ...);
}

template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t width, size_t height,
    typename FunctorContext>
void Repeat(FunctorContext* context)
{
    RepeatImpl<height,Functor>(
        context,
        std::make_index_sequence<width*height>() );
}

will it work when we have variadic types, not indices ?

just wrap the type list in an indexable type-tuple in that case.

But is there a way to handle a few variadics via folds?

as of now, no, I don't think it's possible; packs are always expanded linearly and if more than one pack is present, they are expanded simultaneously and must have the same length, as you already observed. We have no 'direct' language support for 'power-product' pack expansions. If you want a cartesian product of a pair of template parameters, you'll always end up with something like the above ...

like image 83
Massimiliano Janes Avatar answered Oct 11 '22 14:10

Massimiliano Janes