嵌套循环使用元编程展开

Teo*_*lov 9 c++ metaprogramming template-meta-programming c++14 boost-hana

我有许多嵌套循环,小编号I,J,...在编译时已知,例如

for(int i = 0; i < I; ++i) {
    for(int j = 0; j < J; ++j) {
        // ...
        // do sth with (i,j,...)
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要使用大小I,J,...来展开循环,这样我就可以在编译时使用每个坐标组合.

为了澄清,请考虑以下结构并采用大小为I = 2,J = 3的 2个嵌套循环.

template<int... I>
struct C {
     static void f() {
          // do sth
     }
};
Run Code Online (Sandbox Code Playgroud)

我不能使用索引i,j(类似于上面)来索引结构C,因为它们在编译时是未知的.然而,我想要产生的正是我被允许使用索引的情况,例如

C<0,0>::f();
C<0,1>::f();
C<0,2>::f();
C<1,0>::f();
C<1,1>::f();
C<1,2>::f();
Run Code Online (Sandbox Code Playgroud)

只要产生所有组合,我并不特别关注呼叫生成的顺序.生成机制应该推广到任意数量的嵌套循环.

Tar*_*ama 8

您可以通过以树状方式实例化模板,跟踪当前访问的节点来实现此目的.

namespace detail{
    //This is used to store the visited nodes
    template<int...> struct int_pack;

    //Primary template
    template<typename, int... I>
    struct C;

    //This is the leaf node
    template<int... Is>
    struct C<int_pack<Is...>> {
        //The loop body goes here
        static void f() {
            std::cout << __PRETTY_FUNCTION__ << '\n';
        }
    };

    //This is the recursive case
    template <int I, int... Is, int... PIs>
    struct C<int_pack<PIs...>, I,Is...> {
        template <std::size_t... Idx>
        static void f_help (std::index_sequence<Idx...>) {
            //Store the current node in the pack 
            //and call `C::f` for each loop iteration
            (void)std::initializer_list<int> {
                (C<int_pack<PIs...,Idx>,Is...>::f(), 0)... 
            };   
        }

        //Use tag dispatching to generate the loop iterations
        static void f() {
            f_help(std::make_index_sequence<I>{});
        }
    };
}

//Helper alias
template<int... Is>
using C = detail::C<detail::int_pack<>, Is...>;
Run Code Online (Sandbox Code Playgroud)

用法非常简单:

C<2,3>::f();
Run Code Online (Sandbox Code Playgroud)

在Clang上打印:

static void detail::C<detail::int_pack<0, 0>>::f() [I = <>]
static void detail::C<detail::int_pack<0, 1>>::f() [I = <>]
static void detail::C<detail::int_pack<0, 2>>::f() [I = <>]
static void detail::C<detail::int_pack<1, 0>>::f() [I = <>]
static void detail::C<detail::int_pack<1, 1>>::f() [I = <>]
static void detail::C<detail::int_pack<1, 2>>::f() [I = <>]
Run Code Online (Sandbox Code Playgroud)

Live Demo


你可以把它变得更通用,这样你就可以通过lambda将循环体注入到类中,但是如果你只想做一次并且不想引入其他依赖项,上面的解决方案就应该这样做boost::hana.这是更通用版本的可能实现(您可以通过完美转发等来改进它):

namespace detail{
    template<int...> struct int_pack;

    template<typename, int... I>
    struct C;

    template<int... Is>
    struct C<int_pack<Is...>> {
        template <typename Func>
        static void f(const Func& func) {
            func(Is...);
        }
    };

    template <int I, int... Is, int... PIs>
    struct C<int_pack<PIs...>, I,Is...> {
        template <std::size_t... Idx, typename Func>
        static void f_help (std::index_sequence<Idx...>, const Func& func) {
            (void)std::initializer_list<int>{ (C<int_pack<PIs...,Idx>,Is...>::f(func), 0)... };   
        }

        template <typename Func>
        static void f(const Func& func) {
            f_help(std::make_index_sequence<I>{}, func);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

你会像这样使用它:

C<2,3>::f([](int i, int j){
    std::cout << "i " << i << " j " << j << '\n';
});
Run Code Online (Sandbox Code Playgroud)

Live Demo


这是我嘲笑的快速版本boost::hana.有可能更好的方法来做到这一点,但这应该让你知道可以做些什么.

template <typename Func>
void unroll (const Func& func) {
    func();
}

template <std::size_t I1, std::size_t... Is, typename Func>
void unroll (const Func& func) {
    hana::for_each(hana::range_c<std::size_t, 0, I1>,
                   [&](auto x) {
                       unroll<Is...>([x, &func] (auto... xs) { func(x,xs...); });
                   });
}
Run Code Online (Sandbox Code Playgroud)

  • @TeodorNikolov我对图书馆不太熟悉.我会推荐给它一个尝试,寻求帮助对项目的[的Gitter(https://gitter.im/boostorg/hana),如果你会被卡住.我在那里张贴了一两次,创作者非常友好. (2认同)