3

I have never used variadic templates myself, but think I could need them now. Suppose I have a class

class A {
  int Kern;
  template<int> void func_a(int, double) const;
  template<int> void func_b(double, double, char) const;
  template<int> unsigned func_c(float, std::vector<int> const&) const;
public
  /* ... */
  void FuncA(int, double) const;
  void FuncB(double, double, char) const;
  unsigned FuncC(float, std::vector<int> const&) const;
};

where the definitions of A::FuncA() etc. are all of the form

void A::FuncA(int i, double x) const
{
   switch(Kern) {
   case 1: return func_a<1>(i,x);
   case 2: return func_a<2>(i,x);
   case 3: return func_a<3>(i,x);
   /* ... */
   }
 }

I currently implement this switch with a C-macro

#define SwitchKernMacro(KERN,FUNC)   \
switch(KERN) {                       \
case 1: FUNC(1);                     \
case 2: FUNC(2);                     \
case 3: FUNC(3);                     \
/* ... */                            \
}

such that

void A::FuncA(int i, double x) const
{
#define FuncK(KERN) return func_a<KERN>(i,x);
  SwitchKernMacro(Kern,FuncK);
#undef FuncK
}

I like to avoid this C-macro in favour of a variadic template solution, such that the implementation of my functions becomes simply (or similar)

void A::FuncA(int i, double x) const
{ return SwitchKern(Kern,func_a,i,x); }    
void A::FuncB(double a, double b, char c) const
{ return SwitchKern(Kern,func_b,a,b,c); }
unsigned A::FuncC(float f, std::vector<int> const&v) const
{ return SwitchKern(Kern,func_c,f,v); }

How should the template SwitchKern look like?

EDIT

there seems to be some confusion about C++ templates and when they can be used. Suppose, I only have the following very simple functions

class A {
  int Kern;
  template int> void simple() const;
public:
  void Simple() const
  {
    switch(K) {
    case 1: return simple<1>();
    case 2: return simple<2>();
    case 3: return simple<3>();
    default: return simple<0>();
    }
  }
  /* ... */
};

then I can also implement A::Simple() via

class A {
  /* ... */
  template<int> friend struct simple_aux;
};

template<class T, template<int> class SimpleAux>
void Switch(int K, const T* a) {
  switch(K) {
  case 1: return SimpleAux<1>(a)();
  case 2: return SimpleAux<2>(a)();
  case 3: return SimpleAux<3>(a)();
  default: return SimpleAux<0>(a)();
  }
}

template<int k> struct simple_aux
{
  const A*const a;
  explicit simple_aux(const A*a__) : a(a__) {}
  void operator()() { return a->simple<k>(); }
};

void A::Simple() const
{ Switch<A,simple_aux>(K,this); }

However, this solution does not allow for return type different than void and for arbitrary arguments to the functions A::Simple() (passed to A::simple<>()). My question was how to add these functionalities using variadic templates

Walter
  • 44,150
  • 20
  • 113
  • 196
  • Is the first argument to `Func1` determined at compile time or runtime? – Seth Carnegie Sep 25 '12 at 16:09
  • 2
    Then you can't use templates. – Seth Carnegie Sep 25 '12 at 16:13
  • 2
    If *nothing* is determined at compile-time, there's no way to ever get this with templates. Templates are a compile-time mechanism. – R. Martinho Fernandes Sep 25 '12 at 16:16
  • @SethCarnegie If you looked at the code, you would have seen that function argument *types* (and their number) are known at compile time as usual, but not their *values*. – Walter Sep 25 '12 at 16:19
  • @Walter I did look at the code, and what you said doesn't make any difference as far as I can see (which may not be far). – Seth Carnegie Sep 25 '12 at 16:20
  • @Walter: of course YOU DON'T, template parameters **have to** be known at compile time. You can't perform any kind of dynamic binding among a bunch of templates without a switch or stuff like that. – akappa Sep 25 '12 at 16:20
  • @akappa I don't what? "*template parameters have to be known at compile time*" we all know that. "*... without a switch*" **I am using a switch** (as you surely have noted). – Walter Sep 25 '12 at 16:54
  • 1
    I answered to a comment of yours which, apparently, you removed. – akappa Sep 25 '12 at 17:07

2 Answers2

3

The problem is that function templates can't be passed to a template, only class templates. You can work around this with helper classes:

template<template<int i> class Helper, typename... Args>
auto SwitchKern(int Kern, const A &a, Args &&...args)
-> decltype((a.*(Helper<0>::func()))(args...))
{
    switch (Kern) {
    case 1: return (a.*(Helper<1>::func()))(std::forward<Args>(args)...);
    case 2: return (a.*(Helper<2>::func()))(std::forward<Args>(args)...);
    case 3: return (a.*(Helper<3>::func()))(std::forward<Args>(args)...);
    }
}

template<int i>
struct FuncAHelper {
    static decltype(&A::func_a<i>) func() { return &A::func_a<i>; }
};

void A::FuncA(int i, double x) const
{
    return SwitchKern<FuncAHelper, int &, double &>(Kern, *this, i, x);
}

See also Is there a generic way to adapt a function template to be a polymorphic function object?

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • +1 very nice. Is there a way to write a generic helper struct which would work for any function arguments and return type? – Walter Sep 25 '12 at 16:56
  • @Walter sort of, using `decltype` - see above. There's still some boilerplate; because you can't have function template template arguments, you have to write the helper struct by hand, but you could use a macro (!) as `decltype` lets you ignore the signature. – ecatmur Sep 25 '12 at 17:13
  • I eventually got it compiled under gcc 4.7.0 (please allow my edit). – Walter Sep 25 '12 at 17:18
  • `std::function` can wrap almost anything, can it not also help here? – Walter Sep 25 '12 at 17:21
  • @Walter it can wrap any callable concrete object (incl. a function, pointer, etc.) but it can't wrap a template. – ecatmur Sep 25 '12 at 17:22
  • @Walter: The problem is that `std::function` doesn't wrap templates. It wraps (for example) member function pointers, and you want to wrap member function templates. An instantiated member function template can be used to get a member function pointer, but you *need* to instantiate it. – Xeo Sep 25 '12 at 17:22
  • So does this mean that there is something that the compiler could do (since the preprocessor can), but the language doesn't allows us to? – Walter Sep 25 '12 at 17:27
  • @Walter: The preprocessor is a simple text-replacement tool, so yes, the language itself can't do that. – Xeo Sep 25 '12 at 17:29
  • @Walter pretty much; there's no real demand for function template template arguments, as you can always wrap in a `struct`. – ecatmur Sep 25 '12 at 17:31
-1

I think templates are all about compile time solutions and they can't help to solve runtime problems, so if you are using a switch to check a runtime value you can't change to to a template (either variadic or normal).

BigBoss
  • 6,904
  • 2
  • 23
  • 38