5

I have a set of functions that are templated both by an integer type Index and a class type T, that I "partially specialize" in the following manner:

// Integer type
enum Index{One,Two,Three,Four};

// Default implementation
template<int I>
struct Foo{
  template<typename T> static void bar(const T& x){ std::cout <<"default" << endl; }
};

// Template specializations
template<> 
struct Foo<One>{
  template<typename T> static void bar(const T& x){ std::cout << "one" << endl; }
};

This I use to select a particular index at the runtime of the program using a switch-statement (which should result in an efficient look-up table). The switch is independent of T:

template<typename T>
void barSwitch(int k, const T& x){
  switch(k){
    case ONE: Foo<ONE>::bar(x); break;
    case TWO: Foo<TWO>::bar(x); break;
    case THREE: Foo<THREE>::bar(x); break;
  }
}

This works fine, of course, but the class Foo is not the only class for which I would like to apply the switch. In fact, I have a lot of classes that are all templated by the same integer type. So I would like to "template" the class barSwitch above with the function "Foo" as well, so that I can plug in a different class or a different function. The only way I can think of to achieve this is to use a macro:

#define createBarSwitch(f,b) \
template<typename T> \
void barSwitch(int k, const T& x){ \
  switch(k){ \
    case ONE: f<ONE>::b(x); break; \
    case TWO: f<TWO>::b(x); break; \
    case THREE: f<THREE>::b(x); break; \
  }\
}

Is there some better, more C++ style way of doing this?

Joel
  • 1,295
  • 15
  • 30
  • You want to parameterize by a functor type, look at the discussion of functors here: http://stackoverflow.com/q/356950/183203 – antlersoft Feb 16 '12 at 22:29
  • I'm sure you realize that you can't partially specialize functions and that you are fully specializing the `Foo` object but perhaps look into a traits class or the int-to-type idiom to pick the instantiation you want. Overloads may be simpler. – AJG85 Feb 16 '12 at 22:40
  • Right, I know that it's not really partial specialization. I just meant that I specialize the integer type, but keep the `T` type unspecified. – Joel Feb 16 '12 at 22:59

2 Answers2

7

Template template parameters are the key:

enum Index { One, Two, Three, Four };

template <template <Index> class Switcher, typename T>
void barSwitch(int k, const T & x)
{
    switch (k)
    {
        case 1: Switcher<One>::template bar<T>(x); break;
        case 2: Switcher<Two>::template bar<T>(x); break;
        default: assert(false);
    }
}

Usage:

template <Index I> struct Foo
{
    template <typename T> static void bar(const T & x);
};

barSwitch<Foo>(1, Blue);

(It is your responsibility to ensure that every possible template that you substitute for Switcher has a member template bar, of course. If not, you'll get a compile error.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
0
template<template<int> class F>
struct BarFunc {};


template<>
struct BarFunc<Foo> {
    template<Index I, typename T>
    static void call(const T& x) {
        Foo<I>::bar(x);
    }
};


template<template<int> class F, typename T>
void barSwitch(int k, const T& x) {
    switch(k){
    case One: BarFunc<F>::call<One>(x); break;
    case Two: BarFunc<F>::call<Two>(x); break;
    case Three: BarFunc<F>::call<Three>(x); break;
    }
}

You can parametrize which function you want to call by providing a BarFunc specialization.

AndrzejJ
  • 720
  • 5
  • 11