For C++ template, compiler must produce every instance at compile time. So, for every parameter combination (int,double,float), corresponding instance should appear in object file.
It is not possible for your foo to know every parameter combination, as there are infinite amount - so unless you restrict parameter space somehow, the answer to your question is "no".
However, with some template magic it is possible, but not practically useful. I show one specific example as a proof of concept, but please, do not use this in real code.
Lets say
void foo(const char* s, ...);
expects format string like "ffis", where every character specifies a parameter type (double, double, integer, string in this case). We also have a variadic template bar function which prints its arguments:
template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
out << std::forward<Arg>(arg);
using expander = int[];
(void)expander {
0, (void(out << ", " << std::forward<Args>(args)), 0)...
};
out << '\n';
}
void bar() {
std::cout << "no arguments\n";
}
template<typename... Args>
void bar(Args... arguments) {
doPrint(std::cout, arguments...);
}
For foo to work, we will produce at compile time every possible parameter combination up to length N (so, 3^N instances):
//struct required to specialize on N=0 case
template<int N>
struct CallFoo {
template<typename... Args>
static void foo1(const char* fmt, va_list args, Args... arguments) {
if (*fmt) {
using CallFooNext = CallFoo<N - 1>;
switch (*fmt) {
case 'f':
{
double t = va_arg(args, double);
CallFooNext::foo1(fmt + 1, args, arguments..., t);
}break;
case 'i':
{
int t = va_arg(args, int);
CallFooNext::foo1(fmt + 1, args, arguments..., t);
}break;
case 's':
{
const char* t = va_arg(args, const char*);
CallFooNext::foo1(fmt + 1, args, arguments..., t);
}break;
}
} else {
bar(arguments...);
}
}
};
template<>
struct CallFoo<0> {
template<typename... Args>
static void foo1(const char* fmt, va_list args, Args... arguments) {
bar(arguments...);
}
};
void foo(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
//Here we set N = 6
CallFoo<6>::foo1<>(fmt, args);
va_end(args);
}
Main function, for completeness:
int main() {
foo("ffis", 2.3, 3.4, 1, "hello!");
}
Resulting code compiles about 10 seconds with gcc on my machine, but produces the correct string 2.3, 3.4, 1, hello!