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

Need Help Cleaning Up Template Instantation Framework

本文关键字:实例化 框架 帮助      更新时间:2023-10-16

我一直在开发一个框架来帮助实现函数模板实例化。我有一堆函数,出于优化目的,这些函数通过整数值模板化,需要在运行时进行实例化和选择。使用示例如下:

// 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,它使用{ {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2} }的每个组合获取3个整数值。我省略了CreateTemplates的实现,因为它很长,但它是使用boost MPL for_each实现的。上面的代码是我想要使用的每个函数所必需的,虽然它比写512个显式实例化要短,但它仍然有点长。

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

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

注意:使用c++03

编辑:我想回答TarmoPikaro关于我努力实现什么的问题。

我正在使用一个应用程序,其中最多4个任务/线程将共享一个GPU来完成它们的工作(相同的工作,不同的数据)。由于我们的一些CUDA内核使用纹理,我们需要在运行时动态分发可用的纹理。我们一直支持传统的CUDA计算功能,这意味着纹理对象不能作为函数参数传递,必须是静态全局变量。为了给CPU任务/线程提供纹理,我们提供纹理索引,我们的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)

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

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

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()方法的结构,该方法返回重载的函数指针。下面是为您的示例函数定义的一个:

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