将布尔标记更改为模板参数 [英] Change boolean flags into template arguments
问题描述
假设我有一个具有四个布尔标志的多功能函数:
int do_something(int arg,bool flag1,bool flag2 ,bool flag3,bool flag4){
for(int i = 0; i <1000000; i ++){
if(flag1)
//做某事1
if flag2)
//做某事2
if(flag3)
//做某事3
if(flag4)
//做某事4
/ / Do something else 5
}
}
但我不想在内循环中的这些标志上分支的任何代价,所以我将它们改为模板(允许编译器优化掉条件语句):
template< bool flag1,bool flag2,bool flag3,bool flag4>
int do_something_helper(int arg){
for(int i = 0; i <1000000; i ++){
if(flag1)
//做某事1
if(flag2)
//做某事2
if(flag3)
//做某事3
if(flag4)
//做某事4
// Do something else 5
}
}
现在写do_something方法?我知道的唯一方法如下:
int do_something(int arg,bool flag1,bool flag2,bool flag3,bool flag4 ){
if(flag1){
if(flag2){
if(flag3){
if(flag4){
return do_something_helper< true,true,true ,true>(arg);
} else {
return do_something_helper< true,true,true,false>(arg);
}
} else {
if(flag4){
return do_something_helper< true,true,false,true>(arg)
} else {
return do_something_helper< true,true,false,false>(arg);
}
}
// ...你得到图片
}
有没有办法让编译器自动编写上面的代码,所以我不必在我漂亮的代码库中包含这个丑陋的怪物?
我会做一个函子和一组参数,参数索引和范围。然后我将替换索引的参数与 std :: integral_constant< type,value>
并调用函子。 bool
是最简单的,因为范围很明显,所以我先写一个。
可以链接这样的替换和函子,用编译时类型替换 bool
中的每一个。我将使用相同的函子为他们所有,与N重载,esch替换 bool
与 std :: integral_constant< bool,X>
其中 X
是模板
参数。
最后一个将调用final方法,用 integral_constant
而不是 bool
。
请注意,这扩展为实例化的指数数量,所以要小心。
参数操作代码很有趣。 / p>
以下是即时示例 。
有趣的是,执行上述操作的样板可能仍然比较庞大,但希望减少打字错误和更容易测试。
#include< iostream>
#include< tuple>
模板< unsigned ... Is> struct indexes {typedef indexes< Is ...>类型;};
template< unsigned min,unsigned max,unsigned ... Is> struct make_indexes:make_indexes< min,max-1,max-1,Is ...> {};
template< unsigned min,unsigned ... is> struct make_indexes< min,min,Is ...> ;: indexes< Is ...> {};
template< unsigned max,unsigned min = 0>
using Indexes = typename make_indexes< min,max> :: type;
template< unsigned index,typename Functor,typename ... Args,unsigned ... Before,unsigned ... After>
void map_bool_to_compile_time_helper(indices< Before ...>索引< After ...>,Functor& f,std :: tuple< Args ...> args)
{
if(std :: get< index>(args)){
std :: forward< Functor>(f)(std :: get< Before>(args)...,std :: true_type (),std :: get< After>(args)...);
} else {
std :: forward< Functor>(f)(std :: get< Before>(args)...,std :: false_type(),std :: get< After> (args)...)
}
}
template< unsigned index,typename Functor,typename ... Args>
void map_bool_to_compile_time(Functor& f,Args& ... args)
{
map_bool_to_compile_time_helper< index>(Indexes< index>(),Indexes< sizeof ... Args),索引+ 1>(),std :: forward< Functor>(f),std :: make_tuple< Args& );
}
template< typename Functor,unsigned ... indexes>
struct map_bools_to_compile_time_helper;
模板< typename函子,无符号索引,无符号...索引>
struct map_bools_to_compile_time_helper< Functor,index,indexes ...> {
Functor&& F;
map_bools_to_compile_time_helper(Functor&& in):f(std :: forward< Functor>(in)){}
template< typename ... Args>
void operator()(Args& ... args)const {
map_bool_to_compile_time< index>(map_bools_to_compile_time_helper< Functor,indexes ...> {std :: forward< Functor> },std :: forward< Args>(args)...);
}
};
template< typename Functor>
struct map_bools_to_compile_time_helper< Functor> {
Functor&& F;
map_bools_to_compile_time_helper(Functor&& in):f(std :: forward< Functor>(in)){}
template< typename ... Args>
void operator()(Args& ... args)const {
std :: forward< Functor>(f)(std :: forward< Args>(args)...)
}
};
template< unsigned ... Is,typename Functor,typename ... Args>
void map_bools_to_compile_time(indexes< Is ...>,Functor& f,Args& ... args){
map_bools_to_compile_time_helper< Functor,Is ...> {std :: forward< Functor>(f)}(std :: forward< Args>(args)...)
}
struct test {
template< bool b>
void operator()(int x,std :: integral_constant< bool,b>){std :: cout< x < :<< b<<!\ n; }
};
struct test2 {
template< bool b0,bool b1,bool b2>
void operator()(int x,std :: integral_constant< bool,b0>,std :: integral_constant< bool,b1>,std :: integral_constant&
std :: cout<< x < :<< b0 < b1 << b2 << \\\
;
}
};
int main(){
map_bools_to_compile_time(indexes< 1>(),test(),1,true);
map_bool_to_compile_time< 1>(test(),2,false);
map_bools_to_compile_time(indexes< 1,2,3>(),test2(),3,true,false,true);
}
更新支持任意数量的索引的任意数量的参数。 p>
Suppose I have a versatile function with about four boolean flags:
int do_something(int arg, bool flag1, bool flag2, bool flag3, bool flag4) {
for(int i = 0; i < 1000000; i++) {
if(flag1)
// Do something 1
if(flag2)
// Do something 2
if(flag3)
// Do something 3
if(flag4)
// Do something 4
//Do something else 5
}
}
But I don't want to incur any costs for branching on these flags in the inner loop so I change them to templates (allowing the compiler to optimize away the conditionals):
template<bool flag1, bool flag2, bool flag3, bool flag4>
int do_something_helper(int arg) {
for(int i = 0; i < 1000000; i++) {
if(flag1)
// Do something 1
if(flag2)
// Do something 2
if(flag3)
// Do something 3
if(flag4)
// Do something 4
//Do something else 5
}
}
How can I write the do_something method now? The only way I know is as follows:
int do_something(int arg, bool flag1, bool flag2, bool flag3, bool flag4) {
if(flag1) {
if(flag2) {
if(flag3) {
if(flag4) {
return do_something_helper<true,true,true,true>(arg);
}else{
return do_something_helper<true,true,true,false>(arg);
}
}else{
if(flag4) {
return do_something_helper<true,true,false,true>(arg);
}else{
return do_something_helper<true,true,false,false>(arg);
}
}
//... You get the picture
}
Is there some way to get the compiler to write the above code automatically so I don't have to include this ugly monstrosity in my beautiful code-base?
What I would do is take a functor and a pack of arguments and an argument index and a range. Then I would replace the indexed argument with std::integral_constant<type, value>
and call the functor. The bool
case is easiest, as the range is obvious, so I would write that one first.
Then you can chain such replaces and functors to replace each one of the bool
s with compile time types. I would use the same functor for them all, with N overloads, esch replacing one bool
with std::integral_constant<bool, X>
where X
is a template
parameter.
The last one would then call the final method, with integral_constant
instead of bool
.
Note that this expands to an exponential amount of instantiations, so be careful.
The argument manipulation code would be fun to write.
Here is a live example.
Amusingly, the boilerplate to do the above is probably still bulkier, but hopefully less typo-prone and easier to test.
#include <iostream>
#include <tuple>
template<unsigned...Is> struct indexes {typedef indexes<Is...> type;};
template<unsigned min, unsigned max, unsigned...Is> struct make_indexes: make_indexes<min, max-1, max-1, Is...> {};
template<unsigned min, unsigned...Is> struct make_indexes<min, min, Is...>: indexes<Is...> {};
template<unsigned max, unsigned min=0>
using Indexes = typename make_indexes<min, max>::type;
template<unsigned index, typename Functor, typename... Args, unsigned... Before, unsigned... After>
void map_bool_to_compile_time_helper( indexes<Before...>, indexes<After...>, Functor&& f, std::tuple<Args...> args )
{
if (std::get<index>( args )) {
std::forward<Functor>(f)( std::get<Before>(args)..., std::true_type(), std::get<After>(args)... );
} else {
std::forward<Functor>(f)( std::get<Before>(args)..., std::false_type(), std::get<After>(args)... );
}
}
template<unsigned index, typename Functor, typename... Args>
void map_bool_to_compile_time( Functor&& f, Args&&... args )
{
map_bool_to_compile_time_helper<index>( Indexes<index>(), Indexes<sizeof...(Args), index+1>(), std::forward<Functor>(f), std::make_tuple<Args&&...>(std::forward<Args>(args)...) );
}
template<typename Functor, unsigned... indexes>
struct map_bools_to_compile_time_helper;
template<typename Functor, unsigned index, unsigned... indexes>
struct map_bools_to_compile_time_helper<Functor, index, indexes...> {
Functor&& f;
map_bools_to_compile_time_helper(Functor&& in):f(std::forward<Functor>(in)) {}
template< typename... Args>
void operator()( Args&&... args) const {
map_bool_to_compile_time<index>( map_bools_to_compile_time_helper<Functor, indexes...>{std::forward<Functor>(f)}, std::forward<Args>(args)... );
}
};
template<typename Functor>
struct map_bools_to_compile_time_helper<Functor> {
Functor&& f;
map_bools_to_compile_time_helper(Functor&& in):f(std::forward<Functor>(in)) {}
template<typename... Args>
void operator()( Args&&... args) const {
std::forward<Functor>(f)(std::forward<Args>(args)...);
}
};
template<unsigned... Is, typename Functor, typename... Args>
void map_bools_to_compile_time( indexes<Is...>, Functor&& f, Args&&... args ) {
map_bools_to_compile_time_helper<Functor, Is...>{ std::forward<Functor>(f) }( std::forward<Args>(args)... );
}
struct test {
template<bool b>
void operator()( int x, std::integral_constant< bool, b > ) { std::cout << x << ": " << b <<"!\n"; }
};
struct test2 {
template<bool b0, bool b1, bool b2>
void operator()( int x, std::integral_constant< bool, b0 >, std::integral_constant< bool, b1 >, std::integral_constant< bool, b2 > )
{
std::cout << x << ": " << b0 << b1 << b2 << "\n";
}
};
int main() {
map_bools_to_compile_time( indexes<1>(), test(), 1, true );
map_bool_to_compile_time<1>( test(), 2, false );
map_bools_to_compile_time( indexes<1,2,3>(), test2(), 3, true, false, true );
}
Updated with support for any number of arguments at any number of indexes.
这篇关于将布尔标记更改为模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!