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.
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
@ 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