Alex Brooks
Alex Brooks

Reputation: 1151

Explicit Instantiation for Variadic Template Functions

I am writing a library that uses variadic-templated functions, like so:

template<typename ... T>
void func(T ... args) {
  // ...
}

I need to ensure code is generated for this function (i.e. explicit instantiation) for certain types, like so:

template class func<int>;
template class func<int, int>;
template class func<int, int, int>;
// ...

where the max number of int arguments is a non-const maxArgs() (I am unable to change this as it is an external function). I have tried the following:

template<typename ... T>
void f(size_t max, T ... args) { // Generates "infinitely"
  if (sizeof...(T) < max) {
    func(args...);
    f(max, args..., 0);
  }
}

int main(int argc, char** argv) {
  f(maxArgs(), 0);
  // ...
  return 0;
}

However the compiler doesn't have a proper base-case to the function generation recursion, so it fails to compile. I've also tried using non-type templates like so (using some code from here):

template<int ...> struct seq { };
template<int N, int ... Ns> struct gens : gens<N-1, N-1, Ns...> { };
template<int ... Ns> struct gens<0, Ns...> { typedef seq<Ns...> type; };

std::vector<int> params;

template<int ... Ns>
void f(seq<Ns...>) {
  test(std::get<Ns>(params)...);
}

void instantiate(size_t max) {
  for (int i = 1; i < max; ++i) {
    for (int j = 0; j < i; ++j) {
      params.push_back(0);
    }
    f(typename gens<i>::type()); // Fails to compile -- i is not const
    params.clear();
  }
}

int main(int argc, char** argv) {
  instantiate(maxArgs());
}

but this requires a const value, so it fails to compile as well. Is there any way to do this properly having no knowledge of the return value of maxArgs()?

Upvotes: 4

Views: 1510

Answers (2)

Alex Brooks
Alex Brooks

Reputation: 1151

Since I know there is a maximum possible value to maxArgs() (namely 42), I came up with the following solution thanks to the suggestion of @JohnZwinck.

#include <vector>

typedef void (*void_fnptr)();

std::vector<void_fnptr> table;

// Function that needs to be code-generated for certain number of int types
template<typename ... T>
void func(T ... args) {
  // ...
}

template<typename T>
void instantiate(T elem) {
  table.push_back((void_fnptr)func<T>);
}

template<typename T, typename ... Ts>
void instantiate(T first, Ts ... rest) {
  table.push_back((void_fnptr)func<T, Ts...>);
  instantiate(rest...);
}

int main(int argc, char** argv) {
  // 42 int arguments:
  instantiate(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
              0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
  // ...
  return 0;
}

Upvotes: 1

John Zwinck
John Zwinck

Reputation: 249103

No, you cannot possibly generate at compile time templates which depend on a value only known at runtime. You will need to choose some maximum value which is a constant ahead of time (and sometimes not use all the instantiations), or figure out a way to make maxArgs() a compile-time constant. Or compile your code on the fly when it's used!

Since you have more information than we do about this code, perhaps you can think about whether making it be a variadic template is actually required. It seems likely that it isn't, given that the number of template arguments is determined at runtime. It might be better to write a solution which is fully runtime determined, without the variadic template stuff.

Upvotes: 1

Related Questions