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!