1

For a templated function that takes an integer, I wrote the following general-purpose dispatcher:

#define Dispatch_Template(funct_name, max) \
  template<int i> decltype(&funct_name<0>) Dispatch_template_##funct_name (int index) {     \
    return (index == i) ? funct_name <i> : Dispatch_template_##funct_name <i - 1>(index);   \
  } \
  template<> decltype(&funct_name<0>) Dispatch_template_##funct_name <-1>(int) {            \
    return nullptr;                                                                         \
  }                                                                                         \
  decltype(&funct_name<0>) Dispatch_##funct_name (int i) {                                  \
    return Dispatch_template_##funct_name <max>(i);                                         \
  }                                                                                         \

This works and I can do something like this:

template<int some_int> void PrintInt() {
  printf("int is %i\n", some_int);
}

Dispatch_Template(PrintInt, 6);

int main()
{
  for (int i = 0; i < 6; ++i) {
    Dispatch_PrintInt(i)();        
  }
 return 0;
}

But what if I want to pass a typename parameter to my templated function?

For example, say it looks like this:

template<int some_int, typename some_type> void PrintSomeType(some_type arg) {
  // do something
}

I want to be able to do this:

template<typename some_type> void caller(some_type arg) {
  Dispatch_Template(PrintSomeType, some_type, 6);
  for (int i = 0; i < 6; ++i) {
    Dispatch_PrintSomeType(i)(arg);
  }
}

I'm not sure how to do this - I'm running into issues with "a template declaration is not allowed here". (Note that Dispatch_Template here has to be inside the function because the function itself is templated.)

A_K
  • 457
  • 3
  • 11
  • C++ templates can't be declared in function bodies. And this is what expanding Dispatch_Template macro does. I feel that you are looking for a very complex solution to a simple problem. If you described what you are trying to achieve in the grand scheme of things, I could suggest a simpler solution. On general note macros are more trouble than they are worth most of the time. – Ghostrider Apr 14 '17 at 15:58
  • @Ghostrider I am looking for a better way to do this: `template void funct(T var) { ... }` And then `if (i == 0) funct<0>(var);` `else if (i == 1) funct<1>(var);` `...` `else if (i == 100) funct<100>(var):` I can't remove the template from the function itself but I don't want to explicitly type out every possible case. – A_K Apr 14 '17 at 16:02
  • Is seems that Joseph's answer will give you exactly what you need. – Ghostrider Apr 17 '17 at 16:06

1 Answers1

2

You can't get the declarations to work inside a function because templates aren't allowed in block scope. This is a dead end.

So you need a way to declare it outside of the function instead.

Turns out, macros are evil, and just rewriting the macro as a template makes everything just work.

#include <utility>
#include <assert.h>       
#include <iostream>

template<template<int, typename...> class func, typename... Ts>
class Dispatcher {
public:
  using function_ptr = decltype(&func<0, Ts...>::call);

  template<int max=10>
  static function_ptr get_func(int i) {
      assert(i>=0 && i<max);
      return get_func_impl(i, std::make_integer_sequence<int, max>());
  }

private:
  template<int... vals>
  static function_ptr get_func_impl(int i, std::integer_sequence<int, vals...> ) {
      static constexpr function_ptr funcs[] = {&func<vals, Ts...>::call...};
      return funcs[i];
  }

};

template <int i, typename T>
struct Foo {
    static void call(T val) {
        std::cout << "Hello foo " << i << " " << val << std::endl;
    }
};

int main() {
    Dispatcher<Foo, double>::get_func<>(5)(2.3); // output: Hello foo 5 2.3
}

Last step is to create a macro for the required template <...> struct X { call(); }; format. This is required because you can't pass a template function into a template.

note: std::integer_sequence is c++14 only, but you can add a polyfill implementation, e.g. from here. Trying to implement without it is messy with nested partially-specialised structs, because you can't specialize functions inside a template.

Community
  • 1
  • 1
Joseph Ireland
  • 2,465
  • 13
  • 21
  • Thank you! It took me a while, but I got my code working using your example. A note for other people who may try this: keep in mind that you may need to use the "typename" and "template" keywords to make your code unambiguous (especially when the error messages indicate that the compiler is treating your template code as greater-than signs). [Here is what helped me figure it out.](http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords) – A_K May 16 '17 at 20:45