N维嵌套的Metaloops与模板 [英] N-dimensionally nested metaloops with templates

查看:49
本文介绍了N维嵌套的Metaloops与模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用模板元编程来进行N维嵌套的metaloop.嵌套部分微不足道,但是将所有任意数量的迭代索引作为模板参数传递到最内层的循环似乎是有问题的.

I am trying to do N-dimensionally nested metaloops with template metaprogramming. The nesting part is trivial, however passing all the arbitrary number of iteration indices as template parameters to the most-inner loop seems problematic.

一个简单的未嵌套metaloop看起来像:

A simple unnested metaloop looks like:

template <size_t I, size_t N>
struct meta_for
{
    template <typename Lambda>
    inline meta_for(Lambda &&iteration)
    {
        iteration(I);
        meta_for<I+1, N> next(static_cast<Lambda&&>(iteration));
    }
};

template <size_t N>
struct meta_for<N, N>
{
    template <typename Lambda>
    inline meta_for(Lambda &&iteration)
    {
        return;
    }
};

#include <iostream>

int main()
{
    meta_for<0, 10>([&](size_t i) // perform 10 iterations
    {
        std::cout << i << '\n';
    });

    return 0;
}

现在,我想制作一个metaoop,它使用如下方式表示维度(嵌套级别)的N参数:

Now, I want to make a metaloop, which accepts an N parameter denoting the dimensionality (the level of nesting), using like:

#include <iostream>

int main()
{
    // perform 3 dimensionally nested iterations
    // each index goes from 0 to 10
    // so 10x10x10 iterations performed
    meta_for<3, 0, 10>([&](size_t i, size_t j, size_t k)
    {
        std::cout << i << ' ' << j << ' ' << k << '\n';
    });

    return 0;
}

推荐答案

精通此类知识的人可以改善我的答案.

Someone better versed in this stuff can improve my answer.

我的解决方案的要点是声明N个维度,并带有一个起点和一个终点.

The gist of my solution is that you declare N dimensions, with a start and an end.

它以相同的开始和结束在N-1个维度上递归.

It recurses on N-1 dimensions with the same start and end.

到达第一维时,它将实际上开始增加起始位置,并调用传递的函数.

When it reaches the 1st dimension, it will actually begin incrementing the start, calling the passed function.

它将始终尝试传递与维数(其索引)相同的多个参数.

It will always attempt to pass a number of arguments identical to the number of dimensions (their indices).

这样的通话:

meta_for<2, 0, 2>::loop(
    [](size_t i, size_t j)
    {
        std::cout << i << " " << j << std::endl;
    });

将得到这样的输出:

0 0

0 1

1 0

1 1

这是 meta_for 结构,它使用一个帮助程序 iterate :

Here's the meta_for structure, which uses a helper, iterate:

template<size_t D, size_t B, size_t E>
struct meta_for
{
    template<typename Func>
    static void loop(Func&& func)
    {
        iterate<D, B, B, E>::apply(std::forward<Func>(func));
    }
};

和助手:

// a helper macro to avoid repeating myself too much
#define FN template<typename Func, typename... Args> \
             static void apply(Func&& func, Args&&... a)


// Outer loop. S="Self" or "Start". Indicating current index of outer loop. Intent is to iterate until S == E
template<int Dim, size_t S, size_t B, size_t E>
struct iterate
{
    static_assert(S < E && B < E, "Indices are wrong");
    FN
    {
        // outer loop recursive case. Recurse on lower Dimension (Dim-1), and then increment outer loop (S+1)
        iterate<Dim-1, B, B, E>::apply (func, a..., S);
        iterate<Dim, S+1, B, E>::apply (func, a...);
    }
};

// Outer loop base case
template<int Dim, size_t B, size_t E> 
struct iterate<Dim, E, B, E>
{
    FN
    {
        // outer loop base case, End == End. Terminate loop
    }
};

// innter loop. "S" is outer loop's current index, which we need to pass on to function
// "B" is inner loop's (this loop) current index, which needs to iterate until B == E
template<size_t S, size_t B, size_t E>
struct iterate<1, S, B, E>
{
    static_assert(S < E && B < E, "Indices are wrong");
    FN
    {
        // inner loop recursive case. Perform work, and then recurse on next index (B+1)
        func(a..., B);
        iterate<1, S, B+1, E>::apply(func, a...);
    }
};

// inner loop base case
template<size_t S, size_t E>
struct iterate<1, S, E, E>
{
    FN
    {
        // inner loop base case, End == End. Terminate loop
    }
};

// case where zero dimensions (no loop)
template<size_t S, size_t B, size_t E>
struct iterate<0, S, B, E>
{
    static_assert(sizeof(S) == 0, "Need more than 0 dimensions!");
};


更多说明

与其他涉及可变参数模板的解决方案一样,此解决方案也依赖于递归.


More explanation

This solution, like any other involving variadic templates, relies on recursion.

我想在一个外部循环上表达递归,所以我从一个基本情况开始.循环结束.在这种情况下,开始与结束相同:

I wanted to express recursion on an outer loop, so I started with a base case; the end of the loop. This is the case where the start is the same as the end :

template<int Dim, size_t B, size_t E> 
struct iterate<Dim, E, B, E>
{ /*..*/};

请注意,这是< Dim,E,B,E> 的特化.第二个位置指示外循环的当前索引,最后一个位置指示要迭代到(但不包括)的索引.因此,在这种情况下,当前索引与最后一个索引相同,表明我们已完成循环(因此执行了不执行任何操作"功能).

Notice here that this is a specialization for <Dim, E, B, E>. The second position indicates the outer loop's current index, and the last position indicates the index to iterate up to (but not including). So in this case, the current index is the same as the last, indicating we are finished looping (and hence a "do nothing" function).

外部循环的递归情况涉及以下情况:循环索引小于要迭代的索引.用模板术语来说,第二个位置小于第四个位置:

The recursive case for the outer loop involves the scenario where the loop index is less than the index to iterate to. In template terms, the second position is less than the fourth position:

template<int Dim, size_t S, size_t B, size_t E>
struct iterate
{/*...*/}

请注意,这不是专长.

此函数的逻辑是,外循环应发信号通知内循环从其开始就开始执行,然后外循环继续并为内循环重新开始该过程:

The logic of this function is that an outer loop should signal an inner loop to begin executing from its start, and then the outer loop continues and starts the process all over again for inner loops:

iterate<Dim-1, B, B, E>::apply (func, a..., S);
iterate<Dim, S+1, B, E>::apply (func, a...);

在第一行中,请注意第二个模板参数再次为 B ,表示从头开始.这是必要的,因为第二行上的另一种递归情况会增加 S (增加外循环索引).

Notice in the first line that the second template argument is again B, indicating to start at the beginning again. This is necessary because the other recursive case on the second line increments S (incrementing outer loop index).

在整个过程中,我们还在累积传递给函数的参数:

The entire time, we are also accumulating arguments to pass to the function:

::apply(func, a..., S)

将函数与更高维循环的索引一起传递,然后附加当前循环的索引( S ). a 这是可变参数模板.

is passing the function on, along with indices of higher dimension loops, and then appending the current loop's index (S). a here is a variadic template.

当我说内部循环"时,是指最内部的循环.该循环只需简单地递增,直到开始索引到达结束索引为止,而不要尝试在任何较低维度上递归.在我们的例子中,这是我们的 Dim (尺寸)参数为1:

When I say "inner loop", I mean the innermost loop. This loop needs to simply increment until the start index reaches the end index, and not attempt to recurse on any lower dimension. In our case, this is when our Dim (Dimension) parameter is 1:

template<size_t S, size_t B, size_t E>
struct iterate<1, S, B, E>
{/*...*/};

在这一点上,我们最终要调用传递的函数,以及迄今为止积累的所有参数(外部循环的索引)PLUS,最内部循环的索引:

At this point, we finally want to call our passed function, along with all arguments we've accumulated thus far (the indices of the outer loops) PLUS, the index of the innermost loop:

func(a..., B);

然后递归(增量索引)

iterate<1, S, B+1, E>::apply(func, a...);

这里的基本情况是,最内层循环的索引与结束索引相同(并且维数为1):

The base case here is when the innermost loop's index is the same as the end index (AND the dimension is 1):

template<size_t S, size_t E>
struct iterate<1, S, E, E>
{/*...*/};

因此这里的不执行任何操作"功能;因为循环正在终止,所以不应执行任何工作.

Hence the "do nothing" function here; there shouldn't be any work performed because the loop is terminating.

最后,我提供了最后一个专业化功能来捕获用户未指定任何尺寸的错误:

Finally, I included one last specialization to catch a user error where they didn't specify any dimensions:

template<size_t S, size_t B, size_t E>
struct iterate<0, S, B, E>

哪个使用 static_assert 总是失败,因为 sizeof(size_t)不为零:

Which uses static_assert to always fail because sizeof(size_t) is not zero:

static_assert(sizeof(S) == 0, "Need more than 0 dimensions!");

结论

这是一个特定的用例模板元程序.在这里,我们本质上生成N个嵌套的for循环,这些循环都具有相同的开始索引和结束索引,并且我们希望将这些索引传递给函数.我们可以做更多的工作以使 iterate 结构可以独立存在,而无需假设外循环的开始索引和结束索引与内循环的索引相同.

Conclusion

This is a specific use case template meta-program. Where we essentially generate N nested for loops that all have the same start and end indices AND we want to pass those indices to a function. We could do a little more work to make it such that the iterate structure could stand on its own without making the assumption that the outer loop's start and end indices are the same as an inner loop's.

我最喜欢此代码的应用是我们可以使用它制作一个N维计数器.例如,用于N位的二进制计数器(在实时演示中找到).

My favorite application of this code is that we can use it to make an N-dimensional counter. For example, a binary counter for N-bits (found in the live demo).

这篇关于N维嵌套的Metaloops与模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆