2016-07-15 27 views
27

我有一個整數N我知道在編譯時。我也有一個std :: array保存描述N維維數組的形狀的整數。我想在編譯時使用元編程技術來生成嵌套循環,如下所述。如何在編譯時生成嵌套循環

constexpr int N {4}; 
constexpr std::array<int, N> shape {{1,3,5,2}}; 


auto f = [/* accept object which uses coords */] (auto... coords) { 
    // do sth with coords 
}; 

// This is what I want to generate. 
for(int i = 0; i < shape[0]; i++) { 
    for(int j = 0; j < shape[1]; j++) { 
      for(int k = 0; k < shape[2]; k++) { 
       for(int l = 0; l < shape[3]; l++) { 
        f(i,j,k,l) // object is modified via the lambda function. 
       } 
      } 
    } 
} 

注參數Ñ在編譯時已知,但可能合輯之間不可預測的變化,因此我不能硬編碼的環如上。理想情況下,循環生成機制將提供一個接受lambda函數的接口,生成循環並調用產生如上所述等效代碼的函數。我知道可以在運行時用一個while循環和一系列索引編寫一個等價的循環,而且這個問題已經有了答案。然而,我對這個解決方案不感興趣。我也對涉及預處理器魔術的解決方案不感興趣。

+0

是不是你的編譯器上展開循環時優化打開了? – Arunmu

+0

他可能很好地展開一些循環,但這不是重點。我問的是循環生成不循環展開。無論如何,該數組可以保存比上面指定的整數大得多的值。一般來說,完全展開循環是不可能的。 –

+1

使用單循環,並從單循環索引計算「i」,「j」,「k」和「l」等。 –

回答

24

像這樣的東西(注:我的「形」作爲可變參數模板參數設置。)

#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

這不需要'f'是'constexpr'嗎? –

+0

@ Cheersandhth.-Alf-erm,不要這麼認爲。堅持在lambda'cout',應該仍然工作正常... – Nim

+0

@Nim - 很好的解決方案; +1;我看到的唯一缺陷是你必須爲每個'N'開發一個lambda函數('f()','g()'等)。我現在一般不會解決問題,但是如果您可以使用C++ 14,並且lamda必須執行像參數總和這樣的簡單操作,那麼您可以使用可變參數lambda,如'auto f = [](自動我,自動...是){自動ret =我; char unused [] {((ret + = is),'0')...};返回ret; };' – max66

3

我想這正是你問什麼:

#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; 
} 
2

另一個

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

使用示例:

的同樣的事情變
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

4

假設你不想總循環展開,只是代ijk等參數的元組爲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>()); 
    } 
}