How to generate nested loops at compile time

后端 未结 4 1226
逝去的感伤
逝去的感伤 2021-02-01 03:34

I have an integer N which I know at compile time. I also have an std::array holding integers describing the shape of an N-dime

相关标签:
4条回答
  • 2021-02-01 04:07

    Something like this (NOTE: I take the "shape" as a variadic template argument set..)

    #include <iostream>
    
    template <int I, int ...N>
    struct Looper{
        template <typename F, typename ...X>
        constexpr void operator()(F& f, X... x) {
            for (int i = 0; i < I; ++i) {
                Looper<N...>()(f, x..., i);
            }
        }
    };
    
    template <int I>
    struct Looper<I>{
        template <typename F, typename ...X>
        constexpr void operator()(F& f, X... x) {
            for (int i = 0; i < I; ++i) {
                f(x..., i);
            }
        }
    };
    
    int main()
    {
        int v = 0;
        auto f = [&](int i, int j, int k, int l) {
            v += i + j + k + l;
        };
    
        Looper<1, 3, 5, 2>()(f);
    
        auto g = [&](int i) {
            v += i;
        };
    
        Looper<5>()(g);
    
        std::cout << v << std::endl;
    }
    
    0 讨论(0)
  • 2021-02-01 04:14

    Assuming you don't want total loop unrolling, just generation of i, j, k etc. argument tuples for f:

    #include <stdio.h>
    #include <utility>      // std::integer_sequence
    
    template< int dim >
    constexpr auto item_size_at()
        -> int
    { return ::shape[dim + 1]*item_size_at<dim + 1>(); }
    
    template<> constexpr auto item_size_at<::N-1>() -> int { return 1; }
    
    template< size_t... dim >
    void call_f( int i, std::index_sequence<dim...> )
    {
        f( (i/item_size_at<dim>() % ::shape[dim])... );
    }
    
    auto main()
        -> int
    {
        int const n_items = ::shape[0]*item_size_at<0>();
        for( int i = 0; i < n_items; ++i )
        {
            call_f( i, std::make_index_sequence<::N>() );
        }
    }
    
    0 讨论(0)
  • 2021-02-01 04:18

    I suppose this is exactly what you asked for:

    #include <array>
    #include <iostream>
    
    constexpr int N{4};
    constexpr std::array<int, N> shape {{1,3,5,2}};
    
    // Diagnositcs
    
    template<typename V, typename ...Vals>
    struct TPrintf {
            constexpr static void call(V v, Vals ...vals) {
                    std::cout << v << " ";
                    TPrintf<Vals...>::call(vals...);
            }
    };
    
    template<typename V>
    struct TPrintf<V> {
            constexpr static void call(V v) {
                    std::cout << v << std::endl;
            }
    };
    
    
    template<typename ...Vals>
    constexpr void t_printf(Vals ...vals) {
            TPrintf<Vals...>::call(vals...);
    }
    
    // Unroll
    
    template<int CtIdx, typename F>
    struct NestedLoops {
            template<typename ...RtIdx>
            constexpr static void call(const F& f, RtIdx ...idx) {
                    for(int i = 0; i < shape[CtIdx]; ++i) {
                            NestedLoops<CtIdx + 1, F>::call(f, idx..., i);
                    }
            }
    };
    
    template<typename F>
    struct NestedLoops<N-1, F> {
            template<typename ...RtIdx>
            constexpr static void call(const F& f, RtIdx ...idx) {
                    for(int i = 0; i < shape[N-1]; ++i) {
                            f(idx..., i);
                    }
            }
    };
    
    template<typename F>
    void nested_loops(const F& f) {
            NestedLoops<0, F>::call(f);
    }
    
    int main()
    {
            auto lf = [](int i, int j, int k, int l) {
                    t_printf(i,j,k,l);
            };
    
            nested_loops(lf);
            return 0;
    }
    
    0 讨论(0)
  • 2021-02-01 04:18

    Another variant of the same thing:

    template <size_t shape_index, size_t shape_size>
    struct Looper
    {
        template <typename Functor>
        void operator()(const std::array<int, shape_size>& shape, Functor functor)
        {
            for (int index = 0; index < shape[shape_index]; ++index)
            {
                Looper<shape_index + 1, shape_size>()
                    (
                        shape,
                        [index, &functor](auto... tail){ functor(index, tail...); }
                    );
            }
        }
    };
    
    template <size_t shape_size>
    struct Looper<shape_size, shape_size>
    {
        template <typename Functor>
        void operator()(const std::array<int, shape_size>&, Functor functor)
        {
            functor();
        }
    };
    
    template <size_t shape_size, typename Functor>
    void loop(const std::array<int, shape_size>& shape, Functor functor)
    {
        Looper<0, shape_size>()(shape, functor);
    }
    

    Example of use:

    constexpr size_t N {4};
    
    constexpr std::array<int, N> shape {{1,3,5,2}};
    
    void f(int i, int j, int k, int l)
    {
        std::cout
            << std::setw(5) << i
            << std::setw(5) << j
            << std::setw(5) << k
            << std::setw(5) << l
            << std::endl;
    }
    
    // ...
    
    loop(shape, f);
    

    Live demo

    0 讨论(0)
提交回复
热议问题