I'm abusing C++ templates a little and I'm having trouble figuring something out. Let's say I have two types that really should be inherited from a base type, but for speed reasons, I can't afford to have the virtual function overhead (I've benchmarked it, and virtual calls ruin things for me!).
First, here are the two classes I have
template<class DataType> class Class1
{
//Lots of stuff here
}
template<Class DataType> class Class2
{
//The same stuff as in Class1, but implemented differently
}
In a typical oo design, Class1
and Class2
would inherit from IInterface
and I could have a function that looks like this
DoStuff(IInterface& MyInterface)
{
}
But I can't do that, so I've done this
template <class C>
DoStuff(C& c)
{
}
I know it's not pretty, as there's nothing (at the compiler level) to enforce that Class1
and Class2
implement the same interface, but for speed reasons, I'm breaking some of the rules.
What I'd love to do is create a call back function on DoStuff
, but I can't figure out how to make it work with the templates (especially since there's the hidden in there.
For example this works right now
DoStuff(char* filename)
{
switch (//figure out the type i need to make)
{
case 1: return DoStuff(Class1<int>(filename));
case 2: return DoStuff(Class1<double>(filename));
}
}
template<class DataType>
DoStuff(DataType* pdata)
{
return DoStuff(Class2<DataType>(pdata));
}
template<class C>
DoStuff(C c)
{
c.Print();
}
Now I know you're asking, why use Class1
and Class2
? Well the underlying difference between dealing with a file and dealing with memory is so big, that it makes sense to have different classes for the different type of input (rather than just overloading the constructor and having it behave differently for the different inputs). Again, I did benchmark this and it's much faster to have the special cases handled in their own classes rather than having case
s/if
s in every function.
So what I'd like to do is hide a lot of this implementation from the junior developers, I don't want them to have to create three different overloaded DoStuff
s to handle the different inputs. Ideally, I'd just set up some type of callback with #defines
and all they'd need to do is something like create a class called DoStuff
and overload the ()
operator and have the functor do the work.
The trouble I'm having is that the DoStuff
function that does the work is only templatized by <class C>
but C itself is templatized by <class DataType>
and everything I can't figure out how to pass everything around in a generic way. E.g., I cannot use template <class C<DataType>>
or template<template< class DataType> class C>
. It just won't compile.
Does anyone have a good trick to have a generic call back, either a function or a functor (I don't care), with this nested templated class? Basically I want something where I can write a generic function that doesn't care about the class that's storing the data and have that called by a mostly common function that figures out which class to use.
BigSwitch(CallBack,Inputs)
{
switch(//something)
{
case 1: return CallBack(Class1<Type>(Inputs))
case 2: return CallBack(Class2<Type>(Inputs))
}
}
This way I can write one BigSwitch
function and have other people write the CallBack functions.
Any Ideas?
EDIT for clarification for Jalf:
I have two very similar classes, Class1
and Class2
which represent basically the same type of data, however the data store is vastly different. To make it more concrete, I'll use a simple example: Class1
is a simple array and Class2
looks like an array however rather than storing in memory is stores in a file (because it's too big to fit in memory). So I'll call them MemArray
and FileArray
right now. So let's say I wanted the Sum of the arrays. I can do something like this
template <class ArrayType, class ReturnType>
ReturnType Sum(ArrayType A)
{
ReturnType S=0;
for (int i=A.begin();i<A.end();++i)
{
S+=A[i];
}
return S;
}
But now, I need a way to load real data into the array. If it's a memory-based array, I'd do this
MemArray<DataType> M(pData);
and if it's file-baaed, I'd do this
FileArray<DataType> F(filename);
and both of these calls are valid (because the compiler generates both code paths at compile time)
double MS=Sum<MemArray<DataType>,double>(M);
double FS=Sum<FileArray<DataType>,double>(F);
All of this assumes that I know what the DataType is, but for a file based array, I may not know the data type until I open the file and query the header to know what kind of data is in the array.
double GetSum(char* filename)
{
int DataTypeCode=GetDataTypeCode(filename);
switch (DataTypeCode)
{
case 1: return Sum<FileArray<int>,double>(FileArray<int>(filename));
case 2: return Sum<FileArray<double>,double>(FileArray<double>(filename));
}
}
template <class DataType>
double GetSum(DataType* pData)
{
return Sum<MemArray<DataType>,double>(MemArray<DataType>(pData));
}
All of this works, but it requires writing two overloaded GetX
functions and a X
function for everything that I'd want to do. the GetX
functions are basically the same code everytime except for the X
that it calls. So I'd love to be able to write something like
double GetX(CallBackType X, char* filename)
{
int DataTypeCode=GetDataTypeCode(filename);
switch (DataTypeCode)
{
case 1: return X<FileArray<int>,double>(FileArray<int>(filename));
case 2: return X<FileArray<double>,double>(FileArray<double>(filename));
}
}
template <class DataType>
double GetX(CallBackType, DataType* pData)
{
return X<MemArray<DataType>,double>(MemArray<DataType>(pData));
}
so that I could call
GetX(Sum,filename)
then later when someone else wants to add a new function, all they need to do is write the function and call
GetX(NewFunction,filename)
I'm just looking for a way to write my overloaded GetX
functions and my X
functions so that I can abstract way the input/storage from the actual algorithms. Normally, this isn't a hard problem, it's just that I'm having trouble because the X
function contains a template argument that itself is templated. The template<class ArrayType>
also has an implicit ArrayType<DataType>
hidden in there. The compiler is unhappy about that.