I've been working on a framework to help with function template instantations. I have a bunch of functions, templated by integer value for optimization purposes, which need to be instantiated and selected at runtime. A usage example is the following:
// 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);
So in that example, I'm instantiating MyFunction, which takes 3 integer values, with every combination of { {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2}, {7, 0, 3, 4, 2} }
. I've omitted the implementation of CreateTemplates as it's quite long, but it's implemented using boost MPL for_each. The code above is required for every function I want to do this with, and while it's shorter than writing out 512 explicit instantiations, it's still a bit long.
Surprisingly, the longest code that has to be written for each function I want to do this with is the typedef of the function pointer, as many of the functions take 10+ arguments. Is there a way to store these template instantiations in an array of a more generic type by wrapping them somehow?
For the sake of argument, you can assume that the template parameters are always integer values like the example, such that the signatures of the template instantiations are all the same for a given function template. The functions being instantiated are all in global namespace, never member functions (they're actually CUDA kernels). Any other tips to clean this up would be appreciated.
Note: Using c++03
1 Answers
Answers 1
With C++03, I have not been able to find a way to avoid writing the function typedef or a way to make it smaller. With C++11 and decltype you could typedef it like this (assuming you don't have any templates with type parameters):
typedef decltype(&MyFunction<0, 0, 0>) MyFunctionPointer;
On the other hand, you can make some of the code you copy around for every function you instantiate unnecessary. In your example, you have declared a struct MyFunctionTemplateCreator
. This struct can be changed so that it only needs a much smaller struct to provide the value of the function pointer for that instantiation. Here is the more generic version of the 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(); } }; };
You only have to declare this struct once and put it in a header somewhere. It will work for every function that has three identical type parameters. Here is how you would use this struct for the function in your example. First you declare all the mpl::vector
types that are used to provide the values to instantiate the template overloads. Then you create a struct that provides a function()
method which returns the function pointer of the overload. Here is one defined for your example function:
template<int a, int b, int c> struct MyFunctionTypedef { static MyFunctionPointer function() { return &MyFunction<a, b, c>; } };
The InnerStruct
of FunctionTemplateCreator
is what actually is passed into CreateTemplates
. FunctionTemplateCreator
only serves to forward the template parameters to the inner struct. Here is what the CreateTemplates
variable would look like with these new types:
CreateTemplates<FunctionTemplateCreator<int, MyFunctionTypedef, MyFunctionPointer, arrayOfTemplateInstantiations>::InnerStruct, templatesToCreate> unusedVariable;
If you begin using C++11 the function()
method in MyFunctionTypedef
could be made constexpr
.
0 comments:
Post a Comment