【问题标题】:Nested loops unrolling using metaprogramming使用元编程展开嵌套循环
【发布时间】:2016-12-03 20:24:06
【问题描述】:

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

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

我需要使用尺寸I, J, ... 展开循环,以便我可以在编译时 使用每个坐标组合。

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

template<int... I>
struct C {
     static void f() {
          // do sth
     }
};

我不能使用索引 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();

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

【问题讨论】:

    标签: c++ metaprogramming c++14 template-meta-programming boost-hana


    【解决方案1】:

    您可以通过以树状方式实例化模板来做到这一点,跟踪当前访问的节点。

    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...>;
    

    用法很简单:

    C<2,3>::f();
    

    在 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 = <>]
    

    Live Demo


    你可以让它更通用,这样你就可以通过 lambda 将循环体注入到类中,但是如果你只想这样做一次并且不想引入其他依赖项,比如 @ 987654327@。这是更通用版本的可能实现(您可以通过完美转发等来改进它):

    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);
            }
        };
    }
    

    你可以这样使用:

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

    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...); });
                       });
    }
    

    【讨论】:

    • 感谢您在回答中提及boost:hana。在过去的几天里,我一直在了解更多关于它的信息。如果您对该库足够熟悉并且有空闲时间,我想知道您是否可以发布使用它的解决方案。
    • @TeodorNikolov 我对图书馆不是很熟悉。如果您遇到困难,我建议您尝试一下并在项目的Gitter 上寻求帮助。我在那里发过一两次,创作者非常友好。
    • @TeodorNikolov 我在boost::hana 中写了一个关于如何完成它的快速想法,但你最好自己调查一下。
    猜你喜欢
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多