9

小さなサイズのネストループが多数あります。メタプログラミングを使用して展開されたネストされたループ

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はインデックスを使用できない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(); 

特に、すべての組み合わせが生成されている限り、呼び出し世代の順序には関係しません。生成メカニズムは、ネストされたループの任意の数に一般化する必要があります。

答えて

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

使い方は非常に簡単です:

クランオン
C<2,3>::f(); 

この版画:あなたが注入できるように、これは、より汎用的にすることができ

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


ループ本体ラムダを介してクラスに追加しますが、これを一度しか行いたくなく、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); 
     } 
    }; 
} 

あなたがそうのようにこれを使用します。

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

回答に「boost:hana」と言っていただき、ありがとうございます。私は過去2〜3日でそれについてもっと学んできました。あなたがライブラリに慣れていて余裕がある人は、それを使用するソリューションを投稿できるかどうか疑問に思っていました。 –

+2

@TeodorNikolov私はこのライブラリにあまり精通していません。私はそれを試して、あなたが立ち往生するなら、プロジェクトの[Gitter](https://gitter.im/boostorg/hana)の助けを求めることを勧めます。私はそこに1〜2回投稿しており、クリエイターはとてもフレンドリーです。 – TartanLlama

+1

@TeodorNikolov私は 'boost :: hana'でどのようにできるのかという簡単なアイデアを書いていますが、おそらく自分自身で調べることをお勧めします。 – TartanLlama

関連する問題