2016-03-27 12 views
6

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.

+0

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

+0

@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

risposta

1

Con C++ 03, non sono stato in grado di trovare un modo per evitare di scrivere la funzione typedef o un modo per renderla più piccola. Con C++ 11 e decltype si potrebbe typedef in questo modo (ammesso che non si dispone di alcun modello con parametri di tipo):

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

D'altra parte, si può fare una parte del codice si copia in giro per ogni funzione che installi non è necessaria. Nel tuo esempio, hai dichiarato una struct MyFunctionTemplateCreator. Questa struttura può essere modificata in modo tale da richiedere solo una struttura molto più piccola per fornire il valore del puntatore a funzione per tale istanza.Ecco la versione più generica del struct:

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

Basta dichiarare questa struct è una volta e metterlo in un colpo di testa da qualche parte. Funzionerà per ogni funzione che ha tre parametri di tipo identici. Ecco come useresti questa struct per la funzione nel tuo esempio. Prima si dichiarano tutti i tipi mpl::vector utilizzati per fornire i valori per creare un'istanza dei sovraccarichi del modello. Quindi si crea una struttura che fornisce un metodo function() che restituisce il puntatore di funzione del sovraccarico. Ecco quello definito per la funzione ad esempio:

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

La InnerStruct di FunctionTemplateCreator è ciò che in realtà è passato attraverso CreateTemplates. FunctionTemplateCreator serve solo per inoltrare i parametri del modello alla struttura interna. Ecco ciò che la variabile CreateTemplates sarà simile con questi nuovi tipi:

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

Se si inizia a utilizzare C++ 11 il metodo function() in MyFunctionTypedef potrebbe essere fatto constexpr.

+0

Ti ho dato la taglia per aver avuto il tempo di guardarlo e perché non ho trovato nessuna risposta migliore. Grazie dell'aiuto! Sembra che potrebbe essere il momento di aggiornare a C++ 11. – user1777820

Problemi correlati