How to instantiate templated functors F<D> over multiple functors F1,F2 and multiple template parameters D1,D2?

Viewed 67

I need to instantiate a bunch of functors

template<typename DataType>
struct Functor1{
   int a;
   Functor1(int a_){ a = a_; }
//    __device__
   void operator()(DataType &elem) 
       elem.x +=1;
   }
};

template<typename DataType>
struct Functor2{
   int a;
   Functor2(int a_){ a = a_; }
//    __device__
   void operator()(DataType &elem) {
       elem.x +=10;
   }
};

for Cuda by a set of structs D1, D2...:

struct D1{
    int x;
};
struct D2{
    int x;
    int y;
};

I want automatically and explicitly instantiate all of them:

template class Functor1<D1>;
template class Functor1<D2>;
template class Functor2<D1>;
template class Functor2<D2>;

I want a macros/metaprogrammic trick the code above:

#define DATATYPE_LIST(D1)(D2)
#define FUNCTOR_LIST (Functor1)(Functor2)
EXPLICIT_FUNCTOR_INSTANTIATION(FUNCTOR_LIST, DATATYPE_LIST)

How to do that using macroses or SFINAE?

2 Answers

For that I implemented small macros which use boost for_each_product preprocessor:

#include <boost/preprocessor/seq/for_each_product.hpp>

#define MACRO_EXPLICIT_FUNCTOR_INSTANTIATION(r, product) template class BOOST_PP_SEQ_ELEM(0, product)<BOOST_PP_SEQ_ELEM(1, product)>;
#define EXPLICIT_FUNCTOR_INSTANTIATION(functor_list, datatype_list) BOOST_PP_SEQ_FOR_EACH_PRODUCT(MACRO_EXPLICIT_FUNCTOR_INSTANTIATION, (functor_list)(datatype_list))

EXPLICIT_FUNCTOR_INSTANTIATION(FUNCTOR_LIST, DATATYPE_LIST)

If you compile with the flag -E you will see:

template class Functor1<D1>; template class Functor1<D2>; template class Functor2<D1>; template class Functor2<D2>;

Enjoy in the godbolt (be sure that boost library is added)

I want a macros/metaprogrammic trick the code above

Here is a way to do this using templates instead of macros. The below program works for arbitrary number of Functors and Ds. See the different instantiations at the end for different combinations.

//to end recursion                                         #1
template<template<typename>typename T > void f()
{
}
//overloaded for one function parameter                   #2
template<template<typename>typename Functor, typename Type>
void f(Type t)
{
    std::cout<<"non-variadic version called"<<std::endl;            //added for debugging
    Functor<Type>(1)(t);                   
}

//overloaded version that does most of the work
template<template<typename>typename Functor, template<typename>typename... FunctorList,typename Type, typename... TypeList>
void f(Type t, TypeList... tList)
{
     std::cout<<"variadic 2 version called"<<std::endl;            //added for debugging
     f<Functor>(t);   
     f<Functor>(tList...);                                         //basically instantiate the first argument Functor with each element of tList
     //for instantiating all the remaining
     if constexpr(sizeof... (FunctorList)>0 && sizeof...(tList)>0) 
     {
         f<FunctorList...>(tList...);
         f<FunctorList...>(t);
     }
     
     int i = (FunctorList<Type>(1)(t), ... ,  1);

}
int main()
{
    
    f<Functor1, Functor2>(D1(), D2());
   
}

Demo


Below are given the instantiations that will be generated due to different call expressions.

Instantiations for the call expression:

1: f<Functor1, Functor2>(D1(), D2());

Functor1<D1>
Functor2<D2>
Functor3<D1>
Functor3<D3>

2: f<Functor1, Functor2, Functor3>(D1(), D2(), D3());

Functor1<D1>
Functor1<D2>
Functor1<D3>

Functor2<D1>
Functor2<D2>
Functor2<D3>

Functor3<D1>
Functor3<D2>
Functor3<D3>

3. f<Functor1, Functor2, Functor3, Functor4>(D1(), D2(), D3(), D4());

Functor1<D1>
Functor1<D2>
Functor1<D3>
Functor1<D4>

Functor2<D1>
Functor2<D2>
Functor2<D3>
Functor2<D4>

Functor3<D1>
Functor3<D2>
Functor3<D3>
Functor3<D4>

and so one for arbitrary number of Functors and Ds.

This also work for asymmetric call expressions like: f<Functor1, Functor2, Functor3>(D1(), D2(), D3(), D4());

Related