简体   繁体   中英

Change boolean flags into template arguments

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.

You can use templates to organize static dispatch - which will allow to replace branching statement with function overload. This is a rather simple idea, here is a small example:

template <int Val>
struct Int2Type
{
   static const int val_= Val;
};
int do_something(int arg, Int2Type<1>)
{
   // do smth when flag == 1
}

int do_something(int arg, Int2Type<2>)
{
   // do smth when flag == 2
}

... the same principle is applied (by the value of a flag needed overloaded function is called)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM