需要帮助清理模板实例化框架

Need Help Cleaning Up Template Instantation Framework

我一直在研究一个框架来帮助函数模板实例化。我有一堆函数,出于优化目的由整数值模板化,需要在运行时实例化和 selected。用法示例如下:

// Function to instantiate templates of.
template<int a, int b, int c> void MyFunction(float, double){};

// List of values to substitute into each template parameter.
typedef mpl::vector_c< int, 7, 0, 3, 4, 2> valuesToInstantiate;
int numberOfValuesPerParameter = size<valuesToInstantiate>::type::value;

// Function pointer type. Must define type for array to hold template instantiations.
typedef void (*MyFunctionPointer)(float, double);

// Array to hold template instantiations.
// Accessed at runtime to get proper instantiation.
MyFunctionPointer arrayOfTemplateInstantiations[numberOfValuesPerParameter*numberOfValuesPerParameter*numberOfValuesPerParameter];

// Passed to template instantiation framework.
// AddTemplate member function will be called once per template value combo (3 int values).
// templateIndex indicates where to store the instantation in the array.
// templateSequence contains the template value combo (3 int values).
template<int templateIndex, typename templateSequence>
struct MyFunctionTemplateCreator
{
    static void AddTemplate(void)
    {
        // Store template instantiation in array.
        arrayOfTemplateInstantiations[templateIndex] = MyFunction
        <
        mpl::at<templateSequence, mpl::int_<0> >::type::value, 
        mpl::at<templateSequence, mpl::int_<1> >::type::value, 
        mpl::at<templateSequence, mpl::int_<2> >::type::value
        >;
    }
};

// List of lists where each inner list contains values to instantiate
// for the corresponding template parameter. E.g. each value in the first
// inner list will be passed into the first template parameter of MyFunction
typedef mpl::vector< valuesToInstantiate, valuesToInstantiate, valuesToInstantiate > templatesToCreate;

// Call template instantation framework to instantiate templates.
CreateTemplates<MyFunctionTemplateCreator, templatesToCreate> unusedVariable;

// Call proper template instantation at runtime...using index 5 arbitrarily for example.
arrayOfTemplateInstantiations[5](1.5, 2.0);

所以在那个例子中,我正在实例化 MyFunction,它采用 3 个整数值,每个 { {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2} } 的组合。我省略了 CreateTemplates 的实现,因为它很长,但它是使用 boost MPL for_each 实现的。上面的代码对于我要使用的每个函数都是必需的,虽然它比写出 512 个显式实例化要短,但仍然有点长。

令人惊讶的是,我要为每个函数编写的最长代码是函数指针的 typedef,因为许多函数有 10 个以上的参数。有没有办法通过某种方式将这些模板实例化存储在一个更通用类型的数组中?

为了论证,您可以假设模板参数始终是整数值,就像示例一样,这样对于给定的函数模板,模板实例化的签名都是相同的。被实例化的函数都在全局命名空间中,而不是成员函数(它们实际上是 CUDA 内核)。任何其他清理此问题的提示将不胜感激。

注意:使用c++03

编辑:我想回答 TarmoPikaro 关于我正在努力完成的事情的问题。

我正在使用一个应用程序,最多 4 个 tasks/threads 将共享一个 GPU 来完成他们的工作(相同的工作,不同的数据)。由于我们的一些 CUDA 内核使用纹理,我们需要在运行时动态分发可用纹理。我们一直支持传统的 CUDA 计算功能,这意味着纹理对象不能作为函数参数传递,并且必须是静态全局变量。然后,为了向 CPU tasks/threads 提供纹理,我们提供纹理索引,我们的 CUDA 内核具有如下语句:

// (variables t_int_2d_N are texture objects)
if (maskTextureIndex == 0)
    maskValue = tex2D(t_int_2d_0, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)
else if (maskTextureIndex == 1)
    maskValue = tex2D(t_int_2d_1, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)
else if (maskTextureIndex == 2)
    maskValue = tex2D(t_int_2d_2, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)
else if (maskTextureIndex == 3)
    maskValue = tex2D(t_int_2d_3, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)
else if (maskTextureIndex == 4)
    maskValue = tex2D(t_int_2d_4, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)
else if (maskTextureIndex == 5)
    maskValue = tex2D(t_int_2d_5, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)
else if (maskTextureIndex == 6)
    maskValue = tex2D(t_int_2d_6, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)
else if (maskTextureIndex == 7)
    maskValue = tex2D(t_int_2d_7, (float(p) + 0.5f)*maskScale.x + maskShift.x, (float(q) + 0.5f)*maskScale.y + maskShift.y)

在内核中循环使用该语句是不可接受的性能损失。为了避免性能损失,我们通过整数值(表示纹理索引)对内核进行模板化,以便编译出上述条件语句。包含上述代码的内核将使用等于 0-7 的 maskTextureIndex 进行实例化,因此我们在运行时有 8 个不同的内核 select。我们的一些内核最多使用 3 个纹理,我们允许每个纹理类型(例如 float 1D、float 2D、float2 2D、int 3D 等)具有索引 0-7,这意味着我们必须实例化 8*8*8= 512 个不同的内核编译出 3 个不同的条件语句,如上面的那个。每个使用纹理的内核都使用我最初问题中的代码来帮助实例化所有组合。

对于 C++03,我无法找到避免编写函数 typedef 或使其更小的方法。使用 C++11 和 decltype 你可以像这样对它进行类型定义(假设你没有任何带有类型参数的模板):

typedef decltype(&MyFunction<0, 0, 0>) MyFunctionPointer;

另一方面,您可以使您为每个实例化的函数复制的一些代码变得不必要。在您的示例中,您声明了一个结构 MyFunctionTemplateCreator。可以更改此结构,以便它只需要一个小得多的结构来为该实例化提供函数指针的值。这是结构的更通用版本:

template<
    typename Arg,
    template <Arg, Arg, Arg> class TemplateClass,
    typename Func,
    Func* instantiationArray>
struct FunctionTemplateCreator
{
    template<
        int templateIndex,
        typename templateSequence>
    struct InnerStruct
    {
        static void AddTemplate(void)
        {
            instantiationArray[templateIndex] = TemplateClass
                <
                mpl::at<templateSequence, mpl::int_<0> >::type::value,
                mpl::at<templateSequence, mpl::int_<1> >::type::value,
                mpl::at<templateSequence, mpl::int_<2> >::type::value
                >::function();
        }
    };
};

你只需要声明一次这个结构,然后把它放在头文件的某个地方。它适用于具有三个相同类型参数的每个函数。以下是如何将此结构用于示例中的函数。首先声明用于提供值以实例化模板重载的所有 mpl::vector 类型。然后创建一个结构,它提供 function() 方法,其中 returns 重载的函数指针。这是为您的示例函数定义的一个:

template<int a, int b, int c>
struct MyFunctionTypedef
{
    static MyFunctionPointer function()
    {
        return &MyFunction<a, b, c>;
    }
};

FunctionTemplateCreatorInnerStruct 是实际传递给 CreateTemplates 的内容。 FunctionTemplateCreator 仅用于将模板参数转发到内部结构。以下是这些新类型的 CreateTemplates 变量的样子:

CreateTemplates<FunctionTemplateCreator<int, MyFunctionTypedef, MyFunctionPointer, arrayOfTemplateInstantiations>::InnerStruct, templatesToCreate> unusedVariable;

如果你开始使用 C++11,MyFunctionTypedef 中的 function() 方法可以变成 constexpr