9

작은 크기의 여러 중첩 루프가 있습니다. 컴파일시에 예를 들어 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(); 

모든 조합이 생성되는 한 호출 생성 순서와 관련이 없습니다. 생성 메커니즘은 임의의 수의 중첩 루프로 일반화해야합니다.

답변

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


루프 몸체 람다 (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); 
     } 
    }; 
} 

당신은과 같이이를 사용합니다 :

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

답변에서 '부스트 : 하나'를 언급 해 주셔서 감사합니다. 저는 지난 며칠 동안 그것에 대해 더 많이 배웠습니다. 도서관에 익숙하고 여유 시간이 있다면, 그것을 사용하는 솔루션을 게시 할 수 있는지 궁금합니다. –

+2

@TeodorNikolov 나는 도서관에 익숙하지 않다.나는 그걸 시도하고 프로젝트의 [Gitter] (https://gitter.im/boostorg/hana)에 도움을 청하기를 권하고 싶다. 거기에 한 두 번 게시했습니다. 제작자는 매우 친절합니다. – TartanLlama

+1

@TeodorNikolov'boost :: hana'에서 어떻게 할 수 있었는지에 대한 간단한 아이디어를 썼다.하지만 아마 스스로 조사하는 것이 가장 좋다. – TartanLlama