2016-03-27 7 views
6

Ich habe an einem Framework gearbeitet, das bei der Erstellung von Funktionsvorlagen hilft. Ich habe eine Reihe von Funktionen, die zu Optimierungszwecken durch Integer-Werte templatiert werden, die zur Laufzeit instanziiert und ausgewählt werden müssen. Ein Anwendungsbeispiel ist die folgende:Benötigen Sie Hilfe Bereinigen von Vorlagen Instanziierungs-Framework

// 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); 

Also in diesem Beispiel, ich bin Instanziieren MyFunction, die drei ganzzahlige Werte annimmt, mit jeder Kombination von { {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2} }. Ich habe die Implementierung von CreateTemplates weggelassen, da es ziemlich lang ist, aber es wird mit Boost MPL for_each implementiert. Der obige Code wird für jede Funktion benötigt, mit der ich das machen möchte, und obwohl es kürzer ist als das Schreiben von 512 expliziten Instanziierungen, ist es immer noch ein bisschen lang.

Überraschenderweise ist der längste Code, der für jede Funktion geschrieben werden muss, mit der ich dies tun möchte, der Typdef des Funktionszeigers, da viele der Funktionen mehr als 10 Argumente benötigen. Gibt es eine Möglichkeit, diese Template-Instanziierungen in einem Array eines allgemeineren Typs zu speichern, indem Sie sie irgendwie umhüllen?

Aus Gründen der Argumentation können Sie davon ausgehen, dass die Template-Parameter immer ganzzahlige Werte wie im Beispiel sind, so dass die Signaturen der Template-Instanziierungen für eine gegebene Funktionsvorlage alle gleich sind. Die Funktionen, die instanziiert werden, befinden sich alle im globalen Namespace, niemals in Memberfunktionen (sie sind eigentlich CUDA-Kernel). Irgendwelche anderen Tipps, um dies zu reinigen, würden geschätzt werden.

Hinweis: Die Verwendung C++ 03

Edit: Ich wollte TarmoPikaro Frage nach adressieren, was ich versuche zu erreichen.

Ich arbeite mit einer Anwendung, wo bis zu 4 Aufgaben/Threads eine GPU teilen, um ihre Arbeit zu erledigen (gleiche Arbeit, andere Daten). Da einige unserer CUDA-Kernel Texturen verwenden, müssen wir zur Laufzeit dynamisch verfügbare Texturen ausgeben. Wir unterstützen die Legacy-CUDA-Rechenfunktionen, dh Texturobjekte können nicht als Funktionsargumente übergeben werden und müssen statische globale Variablen sein. Zu geben, Texturen CPU-Tasks/Threads dann, wir geben Textur Indizes und unsere CUDA Kerne haben Aussagen wie:

// (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) 

diese Aussage in einer Schleife in einem Kernel zu haben ist eine nicht akzeptable Performance-Verlust. Um den Leistungsverlust zu vermeiden, modellieren wir den Kernel durch einen Integer-Wert (der den Texturindex darstellt), so dass die obige bedingte Anweisung kompiliert wird. Der Kernel, der den obigen Code enthält, wird mit maskTextureIndex gleich 0-7 instanziiert, sodass wir zur Laufzeit aus 8 verschiedenen Kernels auswählen können. Einige unserer Kernel verwenden bis zu 3 Texturen, und wir erlauben jedem Textur-Typ (zB float 1D, float 2D, float2 2D, int 3D usw.) Indizes 0-7 zu haben, was bedeutet, dass wir 8 * 8 * 8 = instanziieren müssen 512 verschiedene Kernel, um 3 verschiedene bedingte Anweisungen wie die obige zu kompilieren. Der Code in meiner ursprünglichen Frage wird pro Kernel verwendet, der Texturen verwendet, um alle Kombinationen zu instanziieren.

+0

Leider ist es so, dass Programmiersprache selbst in irgendeiner Weise zugeschnitten werden kann, in der ursprünglich Autoren der Programmiersprache nicht gedacht haben. Ein solcher Missbrauch oder Missbrauch von Sprache ist für extreme Programmierer möglich, die denken, dass sie Gaia erreicht haben und tun können, was sie wollen, mit der Sprache. Leider wird es schwierig, diesen Code zu pflegen und weiterzuentwickeln, und es ist sehr wahrscheinlich, dass ein anderer Entwickler während der nächsten Iterationen höchstwahrscheinlich Ihre Lösung umschreiben wird. Vielleicht können Sie genauer spezifizieren, was Sie am Ende erreichen wollen? – TarmoPikaro

+0

@TarmoPikaro Endlich haben Sie Ihre Frage angesprochen. Ich bin mir nicht sicher, ob es eine bessere Lösung gibt, ohne auf C++ 11 zu aktualisieren. Hoffe, jemand nimmt das als Herausforderung;). – user1777820

Antwort

1

Mit C++ 03 war ich nicht in der Lage, einen Weg zu finden, die Funktion typedef zu schreiben oder eine Möglichkeit, sie kleiner zu machen. Mit C++ 11 und decltype Sie es so typedef könnte (vorausgesetzt, Sie haben keine Vorlagen mit Typ-Parameter):

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

Auf der anderen Seite, können Sie einen Teil des Codes machen Sie kopieren um für Jede Funktion, die Sie instanziieren, ist unnötig. In Ihrem Beispiel haben Sie eine Struktur MyFunctionTemplateCreator deklariert. Diese Struktur kann geändert werden, sodass sie nur eine viel kleinere Struktur benötigt, um den Wert des Funktionszeigers für diese Instanziierung bereitzustellen.Hier ist die generische Version der Struktur:

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(); 
     } 
    }; 
}; 

Sie müssen nur erklären, diese Struktur einmal und steckt es in einem Header irgendwo. Es funktioniert für jede Funktion, die drei identische Typparameter hat. Hier ist, wie Sie diese Struktur für die Funktion in Ihrem Beispiel verwenden würden. Zuerst deklarieren Sie alle mpl::vector Typen, die verwendet werden, um die Werte bereitzustellen, um die Vorlagenüberladungen zu instanziieren. Dann erstellen Sie eine Struktur, die eine function() Methode bereitstellt, die den Funktionszeiger der Überladung zurückgibt. Hier ist eine für Ihre Beispielfunktion definiert:

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

Die InnerStruct von FunctionTemplateCreator ist, was tatsächlich in CreateTemplates geben wird. FunctionTemplateCreator dient nur dazu, die Vorlagenparameter an die innere Struktur weiterzuleiten. Hier ist, was die CreateTemplates Variablen wie mit diesen neuen Typen aussehen:

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

Wenn Sie beginnen C++ 11 die function() Methode in MyFunctionTypedef mit constexpr gemacht werden kann.

+0

Ich gab Ihnen das Kopfgeld dafür, dass Sie sich die Zeit genommen haben, es anzuschauen und weil ich keine bessere Antwort finden konnte. Danke fürs Helfen! Sieht so aus, als wäre es an der Zeit, auf C++ 11 zu aktualisieren. – user1777820