为模板参数的所有组合实例化函数,在运行时选择即时

时间:2016-02-22 19:38:06

标签: c++ templates

很抱歉,如果之前有人问过,但我找不到这个确切的问题。

我有一个模板化的CUDA内核,如下所示:

template<int firstTextureIndex, int secondTextureIndex, int thirdTextureIndex> __global__ void myKernel

三种纹理索引模板类型的范围为0-7,直到运行时才会知道。我需要实例化该内核的所有512个组合,然后根据纹理索引的运行时值调用正确的模板。

我从来没有写过任何预处理宏,我试图避免它。另一篇文章here展示了如何通过这样做递归地为一个模板变量实例化许多模板:

template<int i>
class loop {
    loop<i-1> x;
}

template<>
class loop<1> {
}

loop<10> l;

我正努力将其扩展到3个变量和一个函数(而不是类)来解决我的问题。即使我弄清楚如何以这种方式实例化所有这些,如何在没有嵌套的switch语句的情况下在运行时实际调用512种可能性中的1种?为了说明,我试图避免的嵌套switch语句如下:

switch(firstTextureIndex)
{
    case 0:
        switch(secondTextureIndex)
        {
            case 1:
                switch(thirdTextureIndex)
                {
                    case 2:
                        myKernel<0, 1, 2><<<grid, block>>>(param1, param2, param3);
                        break;
                }
             break;
        }
    break;
}

如果我弄清楚如何为所有这些实例化0-7,我可以称之为:

myKernel<i, j, k><<<grid, block>>>(param1, param2); 

如果我使i,j和k枚举类型只包含0-7?这样编译器就可以知道所有可能的值,因为我将它们全部实例化它就可以了吗?

请注意,这个三重模板有很好的理由传递纹理索引,但我省略了简洁的解释。任何有关实例化和/或调用此内核的帮助都将非常感激。

编辑:Jarod42提供了一个完全符合我要求的有效解决方案。不幸的是,我现在意识到c ++标准在这里很重要。我正在使用c ++ 98/03和最新稳定版本的boost库,所以使用这些解决方案是理想的。我可能会使用c ++ 11,但由于编译器的限制,c ++ 14已经出局了。

2 个答案:

答案 0 :(得分:3)

您可以执行以下操作:

template <std::size_t I>
void do_job()
{
    myKernel<I / 64, (I / 8) % 8, I % 8>{}();
}

template <std::size_t ... Is>
void callMyKernel(std::index_sequence<Is...>, std::size_t i, std::size_t j, std::size_t k)
{
    std::function<void()> fs[] = {&do_job<Is>...};

    fs[i * 64 + j * 8 + k]();
}

void callMyKernel(std::size_t i, std::size_t j, std::size_t k)
{
    callMyKernel(std::make_index_sequence<512>{}, i, j, k);
}

Demo

答案 1 :(得分:2)

以下代码是 C ++ 98/03 boost.MPL 的实现。肯定有改进的余地(例如隐藏全局指针数组,检查非法组合,......)。

这个想法是递归地遍历整数列表的所有组合,从而为每个组合填充一个函数指针数组。

我之前使用过类似的,更复杂的代码,在运行时选择内核参数(自动调整)的最佳组合,如launch_bounds和其他选项:culgt/runtimechooser

以下是您案例的简化版

#include <iostream>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/at.hpp>

namespace mpl = boost::mpl;

template<int index1, int index2, int index3> void execKernel()
{
    std::cout << "Kernel called with " << index1 << "/" << index2 << "/" << index3 << std::endl;
}

typedef void (*FPTR)();
FPTR ptr[512];

struct NIL
{
public:
    static const int value = 0;
};

template<typename Seq, typename T1, typename T2 = NIL> class MakeSequenceImpl
{
public:
    template<typename T> void operator()(T)
    {
        typedef MakeSequenceImpl<typename mpl::push_back<Seq,T>::type,T2> RunSeq;
        mpl::for_each<T1>( RunSeq() );
    }
};

template<typename Seq> class MakeSequenceImpl<Seq, NIL, NIL>
{
public:
    template<typename T> void operator()(T)
    {
        typedef typename mpl::push_back<Seq,T>::type FinalSeq;

        int index = mpl::at<FinalSeq,mpl::int_<0> >::type::value * 64
                + mpl::at<FinalSeq,mpl::int_<1> >::type::value * 8
                + mpl::at<FinalSeq,mpl::int_<2> >::type::value;

        ptr[index] = execKernel<mpl::at<FinalSeq,mpl::int_<0> >::type::value, mpl::at<FinalSeq,mpl::int_<1> >::type::value, mpl::at<FinalSeq,mpl::int_<2> >::type::value>;
    }
};


template<typename T0, typename T1, typename T2> class MakeSequence
{
public:
    typedef mpl::vector_c<int> Seq;

    MakeSequence()
    {
        typedef MakeSequenceImpl<Seq, T1, T2> RunSeq;
        mpl::for_each<T0>( RunSeq() );
    }
};


void callWrapper( int i, int j, int k )
{
    ptr[i*64+j*8+k]();
}

typedef mpl::vector_c< int, 0, 1, 2, 3, 4, 5, 6, 7 > list1;
typedef mpl::vector_c< int, 0, 1, 2, 3, 4, 5, 6, 7 > list2;
typedef mpl::vector_c< int, 0, 1, 2, 3, 4, 5, 6, 7 > list3;

int main()
{
    MakeSequence<list1,list2,list3> frontend;

    int i,j,k;

    std::cin >> i;
    std::cin >> j;
    std::cin >> k;

    callWrapper(i,j,k);
}