0

Is there a compiler flag or technique to force the generation of one non-variadic function for each call to a variadic function template with a unique signature?

In the following example, the compiler creates variadic templates with signatures int (int, float, double), int (float, double) and int (double).

#include <iostream>

using namespace std;

template<class IntT, class argsf>
IntT SumArgs(const argsf& firstArg)
{
    return static_cast<IntT>(firstArg);
}

template<class IntT, class argsf, class ...argst>
IntT SumArgs(const argsf& firstArg, const argst&... restArgs)
{
    return static_cast<IntT>(firstArg + SumArgs<IntT>(restArgs...));
}

int main()
{
    cout<<"Sum result: " << SumArgs<int>(1, 2.f, 3.5);
    return 0;
}

The first two remain variadic. However, in some circumstances, it might be desirable to avoid the overhead of variadic functions and instead create a unique function definition for each signature listed above. Is there a way to get compilers to do this? The alternative is to write versions of the function that explicitly use 0, 1, 2 ... n arguments for some reasonably large n and duplicate the code by hand, but ideally the compiler could do this heavy-lifting for us.

user1832287
  • 329
  • 3
  • 11
  • 2
    Please avoid `using namespace std;`. It is considered bad practice. See [Why is “using namespace std;” considered bad practice?](https://stackoverflow.com/q/1452721) (In fact, it saves you about -15 characters in this case.) – L. F. Jul 30 '19 at 13:39
  • 4
    I think you misunderstand how templates work. They are just a recipie. When you compile the code, the compiler will stamp out concrete functions (`int (int, float, double)`, `int (float, double)` and `int (double)`) from the templates. – NathanOliver Jul 30 '19 at 13:40
  • 1
    Why do you think there is any overhead at all with templates? – Aconcagua Jul 30 '19 at 13:43
  • @NathanOliver I'm running code in an environment where variadic functions are prohibited. The generated code that is compiled after templates are filled in is still a variadic function, since the compiler backend complains that about using a variadic function. I'm using the ComputeCPP SDK for Ubuntu. – user1832287 Jul 30 '19 at 13:43
  • 2
    I see. I think you would need a tool for this, since the compiler will reject the code no matter what. Does that mean your compiler does not support C++11? – NathanOliver Jul 30 '19 at 13:48
  • 1
    Hm, 'variadic function' is actually something different, like printf; those functions are using the ellipsis syntax, which is far older than variadic templates: `void f(int someThing, ...);` – Aconcagua Jul 30 '19 at 13:50
  • The compiler supports C++11 and C++14, so it compiles fine if I do not use the code from a path that uses the device code generation backend. This backend has restrictions that prevent dynamic memory allocation, so no variadic functions. – user1832287 Jul 30 '19 at 13:50
  • 4
    A variadic template requires no dynamic allocation. They become real function parameters, unlike a variadic function like `printf` – NathanOliver Jul 30 '19 at 13:52
  • @Artyer thanks! Maybe it is a bug in the implementation, since I'm not using any traditional variadic functions. I'll experiment a little more and see if all are disallowed or only those with certain shapes. – user1832287 Jul 30 '19 at 13:52
  • There is no variadic function here anywhere. A variadic template is not a variadic function and will never be instantiated into a variadic function (if you use a proper/correct C++ compiler). Perhaps the "compiler" you're using converts the variadic templates into variadic functions?? – Walter Jul 30 '19 at 13:57
  • Woooops, I had code in the device path that was throwing exceptions and was causing the backend to fail with this error :( I recently added a bunch of variadic function templates so assumed that these were the cause, but it appears that they are fine. This question was helpful in clarifying how these work, thank you for taking the time to respond! – user1832287 Jul 30 '19 at 14:00
  • You have wrote: `since the compiler backend complains that about using a variadic function`. Please edit question and provide that information adding info about environment, compiler and exact error message you see. – Marek R Jul 30 '19 at 14:00
  • Here is the final code: https://github.com/BenjaminTrapani/FunGPU. The variadic template functions are in PortableMemPool.hpp. – user1832287 Aug 25 '19 at 23:47

1 Answers1

2

C++17 introduces fold expressions which are (hopefully) exactly what you are looking for:

template <class R, class... Args>
constexpr R SumArgs(const Args&... args)
{
    return static_cast<R>((args + ...)); // fold expression
}

If you don't want to use variadic templates, you can use an initializer_list:

template <class R, class T>
constexpr R SumArgs(std::initializer_list<T> args)
{
    return std::accumulate(args.begin(), args.end(), R{});
}
L. F.
  • 19,445
  • 8
  • 48
  • 82
  • Or, if you want to enforce at least one summand (as question): `return (static_cast(firstArg) + ... + static_cast(restArgs));` – Aconcagua Jul 30 '19 at 13:44
  • 1
    @Aconcagua The fold expression fires if `args` is empty, FWIW. – L. F. Jul 30 '19 at 13:45
  • 3
    What you write is correct but don't seems to me that respond to the question. The OP problem (she (or he) clarify it in a comment) is that is "running code in an environment where variadic template functions are prohibited". – max66 Jul 30 '19 at 13:46
  • @max66 The fact that variadic templates are prohibited changes the problem fundamentally ... I shall provide the alternative solution and let the OP judge, I guess. – L. F. Jul 30 '19 at 13:51
  • 1
    I see... could works if the list of arguments is (or can be converted in) a list of elements of the same type. In this case, also the good-old `std::vector` could be used, if the OP can't use C++11. – max66 Jul 30 '19 at 13:55