2016-03-27 23 views
6

Pracuję nad strukturą, która pomoże w tworzeniu szablonów funkcji. Mam szereg funkcji, szablonowych według wartości całkowitych dla celów optymalizacji, które muszą być tworzone i wybrane w czasie wykonywania. A Przykład użycia jest następujący:Potrzebujesz pomocy w oczyszczaniu szablonu Wzmocnienie szablonów

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

Więc w tym przykładzie, mam instancji MyFunction, która trwa 3 wartości całkowite, z każdej kombinacji { {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2} }. Pominąłem implementację CreateTemplates, ponieważ jest dość długa, ale została zaimplementowana za pomocą metody boost MPL for_each. Powyższy kod jest wymagany dla każdej funkcji, z którą chcę to zrobić, i choć jest krótszy niż 512 jawnych instancji, to wciąż jest trochę długi.

Co zaskakujące, najdłuższy kod, który musi zostać napisany dla każdej funkcji, którą chcę to zrobić, to typedef wskaźnika funkcji, ponieważ wiele funkcji przyjmuje 10 lub więcej argumentów. Czy istnieje sposób na przechowywanie tych instancji szablonów w tablicy typu bardziej ogólnego poprzez ich owijanie?

Dla celów argumentu można założyć, że parametry szablonu są zawsze wartościami całkowitymi, takimi jak przykład, tak że sygnatury instancji szablonów są takie same dla danego szablonu funkcji. Funkcjonalne instancje znajdują się w globalnym obszarze nazw, nigdy w funkcjach członkowskich (w rzeczywistości są to jądra CUDA). Wszelkie inne wskazówki, jak to wyczyścić, zostaną docenione.

Uwaga: za pomocą C++ 03

Edit: Chciałem zająć pytanie TarmoPikaro o to, co staram się osiągnąć.

Pracuję z aplikacją, w której do 4 zadań/wątków zostanie udostępniony procesor graficzny do wykonania swojej pracy (ta sama praca, inne dane). Ponieważ niektóre nasze jądra CUDA wykorzystują tekstury, musimy dynamicznie rozdawać dostępne tekstury w czasie wykonywania. Utknęliśmy, wspierając starsze funkcje obliczeniowe CUDA, co oznacza, że ​​obiekty tekstur nie mogą być przekazywane jako argumenty funkcji i muszą być statycznymi zmiennymi globalnymi. Dać się tekstur do procesora zadań/wątków wtedy, dajemy nasze indeksy tekstur i jąder CUDA mieć stwierdzenia typu:

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

Mając to oświadczenie w pętli w jądrze jest niedopuszczalne straty wydajności. Aby uniknąć utraty wydajności, szablonujemy jądro przez wartość całkowitą (reprezentującą indeks tekstury) w taki sposób, że kompiluje się powyższą instrukcję warunkową. Jądro, które zawiera powyższy kod, zostanie utworzone za pomocą maskTextureIndex równego 0-7, więc mamy 8 różnych jąder do wyboru w czasie wykonywania. Niektóre nasze jądra używają do 3 tekstur i pozwalamy, aby każdy typ tekstury (np. Float 1D, float 2D, float2 2D, int 3D, itp.) Miał indeksy 0-7, co oznacza, że ​​musimy utworzyć instancję 8 * 8 * 8 = 512 różnych ziaren do kompilacji 3 różnych instrukcji warunkowych, takich jak powyższa. Kod w moim pierwotnym pytaniu jest używany, na jądro, które używa tekstur, aby pomóc utworzyć wszystkie kombinacje.

+0

Niestety jest tak, że sam język programowania można dostosować w dowolny sposób, w jaki oryginalni twórcy języka programowania nie zastanawiali się. Taki niewłaściwy lub nadużywający język jest możliwy dla ekstremalnych programistów, którzy myślą, że dotarli do Gai i mogą robić, co chcą z tym językiem. Niestety, taki kod staje się skomplikowany, aby go utrzymać i dalej rozwijać, i jest wysoce prawdopodobne, że podczas kolejnych iteracji inny programista najprawdopodobniej przerobi twoje rozwiązanie. Czy możesz określić bardziej szczegółowo, co chcesz osiągnąć na końcu? – TarmoPikaro

+0

@ TarmoPikaro W końcu udało mi się odpowiedzieć na twoje pytanie. Nie wiesz, czy istnieje lepsze rozwiązanie bez aktualizacji do C++ 11. Mamy nadzieję, że ktoś podejmie to wyzwanie;). – user1777820

Odpowiedz

1

Z C++ 03, nie byłem w stanie znaleźć sposobu na uniknięcie zapisu funkcji typedef lub sposobu na zmniejszenie jej. Z C++ 11 i decltype można typedef to tak (zakładając, że nie masz żadnych szablonów z parametrami typ):

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

Z drugiej strony, można zrobić niektóre z kodu, który kopiuje się za każda funkcja, którą tworzysz niepotrzebnie. W twoim przykładzie zadeklarowałeś struct MyFunctionTemplateCreator. Ta struktura może zostać zmieniona tak, że potrzebuje tylko znacznie mniejszej struktury, aby zapewnić wartość wskaźnika funkcji dla tej instancji.Oto bardziej ogólna wersja 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(); 
     } 
    }; 
}; 

Trzeba tylko zadeklarować tej struktury raz i umieścić go w nagłówku gdzieś. Będzie działać dla każdej funkcji, która ma trzy identyczne parametry typu. Oto w jaki sposób użyjesz tej struktury dla funkcji w twoim przykładzie. Najpierw należy zadeklarować wszystkie typy mpl::vector, które są używane do podania wartości do utworzenia wystąpienia przeciążenia szablonu. Następnie tworzysz strukturę, która zapewnia metodę function(), która zwraca wskaźnik funkcji przeciążenia. Oto jeden zdefiniowane dla przykładowej funkcji:

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

InnerStruct z FunctionTemplateCreator jest to, co faktycznie jest przekazywana do CreateTemplates. FunctionTemplateCreator służy tylko do przekazania parametrów szablonu do wewnętrznej struktury. Oto co zmienna CreateTemplates będzie wyglądać z tych nowych typów:

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

Jeśli zaczynasz za pomocą C++ 11 metoda function() w MyFunctionTypedef mogą być wykonane constexpr.

+0

Dałem ci nagrodę za poświęcenie czasu na to spojrzenie i ponieważ nie mogłem znaleźć lepszej odpowiedzi. Dzięki za pomoc! Wygląda na to, że nadszedł czas na uaktualnienie do C++ 11. – user1777820

Powiązane problemy