Ho lavorato su un framework per aiutare con le istanze dei modelli di funzione. Ho un sacco di funzioni, basate sul valore intero per scopi di ottimizzazione, che devono essere istanziate e selezionate in fase di runtime. Un esempio di utilizzo è la seguente:Serve aiuto per la pulizia del modello Instantation 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);
Quindi, in questo esempio, sto un'istanza MyFunction, che assume valori interi 3, con ogni combinazione di { {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2} }
. Ho omesso l'implementazione di CreateTemplates poiché è piuttosto lunga, ma è implementata usando boost MPL for_each. Il codice sopra è richiesto per ogni funzione con cui voglio farlo, e mentre è più breve di scrivere 512 istanze esplicite, è ancora un po 'lungo.
Sorprendentemente, il codice più lungo che deve essere scritto per ogni funzione con cui voglio farlo è il typedef del puntatore di funzione, poiché molte delle funzioni richiedono 10+ argomenti. C'è un modo per archiviare queste istanze dei modelli in una matrice di un tipo più generico avvolgendoli in qualche modo?
A titolo di argomento, è possibile assumere che i parametri del modello siano sempre valori interi come nell'esempio, in modo tale che le firme delle istanze del modello siano tutte uguali per un determinato modello di funzione. Le funzioni istanziate sono tutte nello spazio dei nomi globale, mai funzioni membro (in realtà sono kernel CUDA). Qualsiasi altro suggerimento per pulire questo sarebbe apprezzato.
Nota: L'uso C++ 03
Edit: ho voluto affrontare la domanda di TarmoPikaro su quello che sto cercando di realizzare.
Sto lavorando con un'applicazione in cui fino a 4 attività/thread condivideranno una GPU per fare il loro lavoro (stesso lavoro, dati diversi). Dal momento che alcuni dei nostri kernel CUDA utilizzano trame, abbiamo bisogno di distribuire dinamicamente le trame disponibili in fase di runtime. Siamo bloccati nel supportare le capacità di calcolo CUDA legacy, il che significa che gli oggetti texture non possono essere passati come argomenti di funzione e devono essere variabili globali statiche. Per dare le texture per CPU compiti/le discussioni allora, dare indici di struttura e le nostre kernel CUDA avere dichiarazioni come:
// (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)
Avere questa affermazione in un ciclo in un kernel è un perdita di prestazioni inaccettabili. Per evitare la perdita di prestazioni, modelliamo il kernel in base al valore intero (che rappresenta l'indice di trama) in modo tale che l'istruzione condizionale sopra riportata sia compilata. Il kernel che contiene il codice sopra verrà istanziato con maskTextureIndex uguale a 0-7, quindi abbiamo 8 diversi kernel da selezionare a runtime. Alcuni dei nostri kernel usano fino a 3 trame e consentiamo ogni tipo di texture (ad es. Float 1D, float 2D, float2 2D, int 3D, ecc.) Per avere indici da 0 a 7, ovvero dobbiamo istanziare 8 * 8 * 8 = 512 diversi kernel per compilare 3 diverse istruzioni condizionali come quella sopra. Il codice nella mia domanda originale è usato, per kernel che usa le trame, per aiutare a istanziare tutte le combinazioni.
Sfortunatamente è così che il linguaggio di programmazione stesso può essere adattato in qualche modo, in cui gli autori originali del linguaggio di programmazione non hanno pensato. Tale linguaggio abusivo o abusante è possibile per i programmatori estremi che pensano di aver raggiunto Gaia e possono fare tutto ciò che vogliono con la lingua. Sfortunatamente tale codice diventa complesso da mantenere e sviluppare ulteriormente, ed è altamente possibile che durante le successive iterazioni un altro sviluppatore probabilmente riscriverà la tua soluzione. Potrebbe essere possibile specificare più in dettaglio cosa si vuole raggiungere alla fine? – TarmoPikaro
@TarmoPikaro Finalmente sono riuscito a rispondere alla tua domanda. Non sono sicuro che ci sia una soluzione migliore senza l'aggiornamento a C++ 11. Sperando che qualcuno lo consideri una sfida;). – user1777820