Viridya
Viridya

Reputation: 640

Variadic Template type from dynamic values

I am writing some sort of source to source compiler and at a moment, I produce a succession of if statements that allow me to instantiate the correct template object. These objects require a bool..., so some may need juste one, others many. It looks like :

if(unknow_at_compilation==1){
    if(unknown2 == 3){
        My_obj<true> A;
        My_obj<true, false> B;
        /*do something X*/
    }else{
        My_obj<true> A;
        My_obj<false, false> B;
        /*do same thing X*/
}else{
    if(unknown2 == 3){
        My_obj<false> A;
        My_obj<true, true> B;
        /*do same thing X*/
    }else{
        My_obj<false> A;
        My_obj<false, true> B;
        /*do same thing X*/
    }
}

but with much more conditions and objects. I can not use polymorphism and pointer since performance is a critical point in my application and the object A and B are heavily used.

Instead of that complexe succession if if I would like to use metaprogrammation. However, I face some serious difficulties... During my generation, I know the number of objects I need and how much "bool" it needs.

The first step was to encapsulate the "do something" part in a template structure taking sets of boolean :

template<bool... val>
struct bool_set{};

template<typename T1, typename T2>
struct struct_do_something;

template<bool... b1, bool... b2>
struct struct_do_something<bool_set<b1...>, bool_set<b2...>>
{
    static void do_something(){
        My_obj<b1...> i;
        My_obj<b2...> j;
        /*Do something*/
    }

};

This parts works and I can call it like that (for example) : struct_do_something<bool_set<true, true>, bool_set<false, false>>::do_something();

Then, I've written a structure that generate a single bool_set using the conditions.

template<bool... static_vals> //bool values of the currently created bool_set
struct bool_set_generator{

    template<typename... Bool>
    static void select(bool v1, Bool... dynamic_vals) //Conditions in parameters
    {
        if(v1)
            return bool_set_generator<static_vals..., true>::select(dynamic_vals...);
        else
            return bool_set_generator<static_vals..., false>::select(dynamic_vals...);
    }

    static void select(){
        /*I have a bool_set here -> I can "do something"*/
        struct_do_something<bool_set<static_vals...>>::do_something();
    }
};

Obviously, it's not finish : I can generate a single bool_set so my idea was to store the bool_set already created + the one currently created like that :

template <typename... Created, bool... static_val>

but the compiler (g++ 5.4 with -std=c++11) tells me parameter pack ‘Created’ must be at the end of the template parameter list and I switch it with the other, it says the same...

Does someone has an idea ? In the end, I would like to call my function like that (or equivalent):

generate_the_do_something<>::call({cond1, cond2}, {cond3, cond4, cond5});

Each initializer list is a bool_set and this generate_the_do_something is a structure (of a function ?) that will create the bool_set and call the do_something() with them.

Upvotes: 2

Views: 148

Answers (1)

max66
max66

Reputation: 66200

If you accept to separate the set of bools with a specific separator, as in the following example

call_do_something(false, true, end_set{}, false, false, true, end_set{});

using std::tuple (to pack the completed bool_set) it's relatively easy.

See the following working (at least... compiling) example

#include <tuple>

template<bool...>
struct bool_set{};

struct end_set{};

template <bool...>
struct My_obj{};

template <typename, typename>
struct do_something;

template <bool... bs1, bool... bs2>
struct do_something<bool_set<bs1...>, bool_set<bs2...>>
 {
   static void func ()
    {
      My_obj<bs1...> mo1;
      My_obj<bs2...> mo2;

      (void)mo1; // just to avoid a lot warnings
      (void)mo2; // just to avoid a lot warnings

      // do something else
    }
 };

template <typename, typename>
struct gtds; // ex generate_the_do_something

template <typename ... Ts, bool ... Bs>
struct gtds<std::tuple<Ts...>, bool_set<Bs...>>
 {
   template <typename ... As>
   static void call (bool const & val, As const & ... as)
    {
      if ( val )
         gtds<std::tuple<Ts...>, bool_set<Bs..., true>>::call(as...);
      else
         gtds<std::tuple<Ts...>, bool_set<Bs..., false>>::call(as...);
    }

   template <typename ... As>
   static void call (end_set const &, As const & ... as)
    { gtds<std::tuple<Ts..., bool_set<Bs...>>, bool_set<>>::call(as...); }

   template <bool bsEmpty = (sizeof...(Bs) == 0U)>
   static typename std::enable_if< ! bsEmpty >::type call ()
    { do_something<Ts..., bool_set<Bs...>>::func(); }

   template <bool bsEmpty = (sizeof...(Bs) == 0U)>
   static typename std::enable_if< bsEmpty >::type call ()
    { do_something<Ts...>::func(); }
 };

template <typename ... Ts>
void call_do_something (Ts const & ... ts)
 { gtds<std::tuple<>, bool_set<>>::call(ts...); }


int main ()
 {
   call_do_something(false, true, end_set{}, false, false, true);
   call_do_something(false, true, end_set{}, false, false, true, end_set{});
 }

Observe the use of SFINAE (std::enable_if over call() without parameters) to permit the call of call_do_something() with or without ending end_set{}.

Upvotes: 1

Related Questions