将布尔标记更改为模板参数 [英] Change boolean flags into template arguments

查看:103
本文介绍了将布尔标记更改为模板参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个具有四个布尔标志的多功能函数:

  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 bools 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆