4

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()?

Community
  • 1
  • 1
Alex Brooks
  • 1,151
  • 1
  • 10
  • 39
  • Seeing as this is probably not possible, how do compilers (specifically gcc) handle code generation of C-style variable-argument functions using an ellipsis and va_list? For example: `void func(int a, ...) { ... }` – Alex Brooks Sep 06 '14 at 08:55

2 Answers2

1

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.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • So the reason I need the code to be generated in such a manner is because I have to provide another library the function pointers for the various instances of `func`, not knowing which specific instances will be used by the application using my own library. Also, the library I am passing function pointers to is responsible for `maxArgs()`. – Alex Brooks Sep 06 '14 at 08:46
  • What's the permissible range of `maxArgs()`? What are the exact requirements of the other library in terms of how you need to give it the function pointers? Can it accept pointers to C-style variadic functions (like printf), or does it require pointers to regular functions taking 1..maxArgs arguments? If the latter, how would any such library ever have been used properly? It seems hard to imagine. – John Zwinck Sep 06 '14 at 09:00
  • The possible range is `8 <= maxArgs() <= 42`. The library takes the functions in the form of an array of function pointers where the type must be type cast to `void (*fn)()`; the functions don't require templates and can use C-style variadic functions – Alex Brooks Sep 06 '14 at 14:21
  • @AlexBrooks: Then it seems you should instantiate your function template for all values up to 42 and be done with it. If the library called your function with a first argument being the number of following arguments, you could write a single, non-template, printf-style function. As it stands, I am amazed at the design of this library you're using: it seems designed to confound its users. – John Zwinck Sep 06 '14 at 14:51
  • Thank you. For more context, the library is GASNet and the portion of my library I've described is a wrapper around their active messages feature (for various reasons). – Alex Brooks Sep 06 '14 at 15:08
  • I see now: an HPC communication library. It starts to make a *little* more sense now. Well, even in 1994 when GASNet was released, C programmers knew that you must always pass an argument telling a variadic function how many (and what type of) arguments it will receive. That they did not provide one here implies that they never attempted to write a generic handler function as you are doing. Probably because it's only needed if you're writing a wrapper, and nobody likes to think their baby is going to be wrapped up by someone else. :) A design defect; you'll have to hard-code maxArgs. – John Zwinck Sep 07 '14 at 00:59
1

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;
}
Alex Brooks
  • 1,151
  • 1
  • 10
  • 39