c ++可扩展的lambda函数分组在任意数量的块中
c++ scalable grouping of lambda functions in blocks of an arbitrary number
我必须执行多个 lambda 函数,但每个 N
lambda 函数也必须是 prologue()
函数 运行。 lambda 的数量可以任意大,并且 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
![=19 编写一个处理程序=]
是否可以进行一些神奇的元编程来涵盖所有可能的情况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... );
}
编辑:发布了相关的。
可预测:
接受一个函数对象。 Returns 一个带有多个参数的函数对象,一次将它们传递给第一个对象。
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)...));
}
采用索引序列。 Returns 一个接受函数对象的函数,然后传递该对象编译时索引:
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) 代码。
使用辅助结构怎么样?
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...); }
更简单的解决方案
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)... };
}
--编辑-- 在 fn()
的调用前面添加了 (void)
以避免 Yakk 在评论中解释的逗号劫持技巧(谢谢!)。
我必须执行多个 lambda 函数,但每个 N
lambda 函数也必须是 prologue()
函数 运行。 lambda 的数量可以任意大,并且 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
![=19 编写一个处理程序=]
是否可以进行一些神奇的元编程来涵盖所有可能的情况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... );
}
编辑:发布了相关的
可预测:
接受一个函数对象。 Returns 一个带有多个参数的函数对象,一次将它们传递给第一个对象。
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)...));
}
采用索引序列。 Returns 一个接受函数对象的函数,然后传递该对象编译时索引:
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) 代码。
使用辅助结构怎么样?
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...); }
更简单的解决方案
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)... };
}
--编辑-- 在 fn()
的调用前面添加了 (void)
以避免 Yakk 在评论中解释的逗号劫持技巧(谢谢!)。