1

私はいくつかのラムダ関数を実行する必要がありますが、すべての各Nラムダprologue()関数も実行する必要があります。ラムダの数は任意に大きくすることができ、コンパイル時にはNが知られています。このような何か:任意の数のブロックでラムダ関数のスケーリング可能なグループ化

static void prologue(void) 
{ 
    cout << "Prologue" << endl; 
} 

int main() 
{ 
    run<3>(// N = 3 
     [](){ cout << "Simple lambda func 1" << endl; }, 
     [](){ cout << "Simple lambda func 2" << endl; }, 
     [](){ cout << "Simple lambda func 3" << endl; }, 
     [](){ cout << "Simple lambda func 4" << endl; }, 
     [](){ cout << "Simple lambda func 5" << endl; }, 
     [](){ cout << "Simple lambda func 6" << endl; }, 
     [](){ cout << "Simple lambda func 7" << endl; } 
    ); 
} 

出力:

Prologue 
Simple lambda func 1 
Simple lambda func 2 
Simple lambda func 3 
Prologue 
Simple lambda func 4 
Simple lambda func 5 
Simple lambda func 6 
Prologue 
Simple lambda func 7 
End 

剰余を適切に処理しなければなりません。

私は以下の解決策に達しましたが、わかりましたが、私はのそれぞれNのハンドラを書かなければならないので、非常にスケーラブルではありません!

Nの可能性をすべて網羅するために、いくつかのマジックメタプログラミングが可能ですか?私は焦点を失ったし、この問題を解決するために全く異なるアプローチがありますか?コンパイル時にすべてを解決する必要があります。

#include <iostream>  
using namespace std; 

static void prologue(void); 

// Primary template 
template< int N, typename... Args> 
struct Impl; 

// Specialitzation for last cases 
template< int N, typename... Args > 
struct Impl 
{ 
    static void wrapper(Args... funcs) 
    { 
     Impl<N-1, Args...>::wrapper(funcs...); 
    } 
}; 

// Specilitzation for final case 
template<int N> 
struct Impl<N> 
{ 
    static void wrapper() 
    { 
     cout << "End" << endl; 
    } 
}; 

template< typename Arg1, typename... Args > 
struct Impl<1, Arg1, Args...> 
{ 
    static void wrapper(Arg1 func1, Args... funcs) 
    { 
     prologue(); 
     func1(); 

     Impl<1, Args...>::wrapper(funcs...); 
    } 
}; 

template< typename Arg1, typename Arg2, typename... Args > 
struct Impl<2, Arg1, Arg2, Args...> 
{ 
    static void wrapper(Arg1 func1, Arg2 func2, Args... funcs) 
    { 
     prologue(); 
     func1(); 
     func2(); 

     Impl<2, Args...>::wrapper(funcs...); 
    } 
}; 

template< typename Arg1, typename Arg2, typename Arg3, typename... Args > 
struct Impl<3, Arg1, Arg2, Arg3, Args...> 
{ 
    static void wrapper(Arg1 func1, Arg2 func2, Arg3 func3, Args... funcs) 
    { 
     prologue(); 
     func1(); 
     func2(); 
     func3(); 

     Impl<3, Args...>::wrapper(funcs...); 
    } 
}; 

// Static class implementation wrapper 
template< int N, typename... Args > 
static void run(Args... funcs) 
{ 
    Impl<N, Args...>::wrapper(funcs...); 
} 

EDIT:関連questionを掲載。

答えて

5

簡単なソリューション

template <std::size_t N, typename ... Ts> 
void run (Ts const & ... fn) 
{ 
    using unused = int[]; 

    std::size_t i { N-1U }; 

    (void)unused { 0, ((++i % N ? 0 : (prologue(), 0)), (void)fn(), 0)... }; 
} 

--EDIT--はコメントでYakkによって説明コンマハイジャックのトリックを避けるためにfn()の呼び出しに前に(void)を追加しました(ありがとう!) 。

+0

これは美しいです。 –

+0

@yurikilochek - カンマ演算子のパワーを過小評価しないでください:-) – max66

+0

あなたは '' fn'のretirn型を知らないので、 '' operator ''をオーバーロードする可能性があります。 – Yakk

1

使用可能:

関数オブジェクトをとります。多数のargをとり、一度に1つずつ最初のオブジェクトに渡す関数オブジェクトを返します。

template<class F> 
void foreach_arg(F&&f){ 
    return [f=std::forward<F>(f)](auto&&...args){ 
    using discard=int[]; 
    (void)discard{0,(0,void(
     f(decltype(args)(args)) 
    ))...} 
    }; 
} 

その後、我々は、単にインデックスを追跡:

template<std::size_t N, class...Args> 
void run(Args&&...args){ 
    std::size_t i = 0; 
    foreach_arg([&](auto&&arg){ 
     if (!(i%N))prologue(); 
     ++i; 
     arg(); 
    } 
)(args...); 
} 

、より複雑なソリューション。これは、インデックスをconstexpr値として計算します。

まず、パックからn番目の引数を取得:

template<std::size_t N, class...Args> 
decltype(auto) nth(Args&&...args){ 
    return std::get<N>(std::forward_as_tuple(std::forward<Args>(args)...)); 
} 

はインデックスシーケンスをとります。

template<std::size_t...Is> 
auto index_over(std::index_sequence<Is...>){ 
    return [](auto&&f)->decltype(auto){ 
    return decltype(f)(f)(std::imtegral_constant<std::size_t,Is>{}...); 
    }; 
} 

はあなたが0...N-1上に呼び出すことができます、一般的なケースである::さて、実際の問題の特定のコード

template<std::size_t N> 
auto index_upto(std::integral_constant<std::size_t,N> ={}){ 
    return index_over(std::make_index_sequence<N>{}); 
} 

そのオブジェクトコンパイル時のインデックスを渡し、関数オブジェクトをとる関数を返します。 :

template<std::size_t N, class...Args> 
void run(Args&&...args){ 
    index_upto<sizeof...(Args)>()(
    foreach_arg([&](auto I){ 
     if (!(I%N))prologue(); 
     nth<I>(std::forward<Args>(args)...)(); 
    }) 
); 
} 

おそらくtpyosです。

このファイルもコンパイルが遅くなることがあります。 O(n^2)コードを生成します。

1

ヘルパー構造体を使用するとどうなりますか?

template <std::size_t N, std::size_t M> 
struct runH 
{ 
    template <typename T0, typename ... Ts> 
    static void func (T0 const & f0, Ts const & ... fn) 
    { 
     f0(); 
     runH<N, M-1U>::func(fn...); 
    } 

    static void func() 
    { } 
}; 

template <std::size_t N> 
struct runH<N, 0> 
{ 
    template <typename ... Ts> 
    static void func (Ts const & ... fn) 
    { 
     if (sizeof...(fn)) 
     prologue(); 

     runH<N, N>::func(fn...); 
    } 
}; 

template <std::size_t N, typename ... Ts> 
void run (Ts const & ... fn) 
{ runH<N, 0>::func(fn...); } 
関連する問題