views:

294

answers:

2

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 cases/ifs 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 DoStuffs 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.

A: 

As to your question about a "generalized callback" you can use a boost::function but that essentially uses virtual functions under the covers (it may not - but at least a similar concept) so the performance difference you are looking for won't be there (in fact boost::function will probably be slower because of heap allocation).

Greg Rogers
+3  A: 

Focusing on the initial part of your question (why you're not just using inheritance):

A common way to do compile-time polymorphism and give access to the derived class' members through the base class is through the CRTP pattern.

template <typename T>
class IInterface {
  void DoStuff() {
    void static_cast<T*>(this)->DoStuff()
  }
};

class Class1 : IInterface<Class1> {
  void DoStuff(){...}
}

Would that solve your problem?

Edit: By the way, I'm glad I could help, but next time please try to structure your question a bit more.

I really had no clue what you were asking, so this was just a stab in the dark, based on the first 3 lines of your question. ;)

You never really explain what you're trying to achieve, only what your non-functioning workaround looks like. Start out stating the problem, since that's what we really need to know. Then you can provide details about your current workarounds. And when posting code, add some context. Where are DoStuff() called from, and why would junior developers need to define them? (You've already done that, haven't you?)

What would said junior developers be doing with this code in the first place?

And it's confusing that you provide the specific cases (1 and 2), but not the switch statement itself (//something)

You'll get a lot more (and better and faster) answers next time if you try to make it easy for the person answering. :)

jalf
This is looking pretty close to what I want. I'm worried about the indirection a little though, right now, I have some great speed advantages with everything being inlined. But this is probably as close as I'm going to get. I didn't know about the CRTP pattern.
miked
The nice thing about this pattern is that this simple indirection is almost guaranteed to be inlined. As long as the function definition is visible at the call site and it is a short function, any half decent C++ compiler will inline it. So there will be zero overhead from this in practice.
jalf