kien
kien

Reputation: 195

How to disambiguate a variadic function with another in C++

Let's say I want to have a function overloaded by two versions like this:

a) void query(const string& s); which makes an SQL query to a server.

b) void query(const string& s,...); which builds a query string given by a format string and arguments to be substituted. Internally, this version looks like (I hide the details to not over-complicate the question):

va_list vargs;
va_start(vargs, s);
// ... call vsnprintf to build the query string
// ... call the first version with the query string
va_end(vargs);

Note that I also want this to work in both MSVC and GCC. Of course, by writing as above, I cannot go for the following call because of ambiguity:

query("...");

To resolve the ambiguity in this case, I have tried several ways, but none of them works:

1) Rewrite them as:

void query(const string& s) {
// ...
}

template<typename Value>
void query(const string& s, Value value,...) {
    va_list vargs;
    va_start(vargs, s);
    // ...
}

This compiles and works fine in MSVC, but GCC complains with a warning:

"second parameter of va_start is not last named argument"

Even if I ignore that warning, it doesn't work. Somehow vargs cannot capture value parameter for whatever I try: va_start(vargs, s) or va_start(vargs, value). It seems to me that GCC always takes only unnamed parameters into vargs no matter what we provide as 2nd parameter to va_start.

2) Rewrite them as

void query(const string& s) {
// ...
}

template<typename... Values>
enable_if<(sizeof...(Values) > 0), void>::type
query(const string& s, Values value...) {
    va_list vargs;
    va_start(vargs, s);
    // ...
}

Again, this compiles and works with MSVC. But GCC complains with an error that the 2nd version is a variadic template rather than variadic function, and va_start is not allowed to be used there. It seems that va_start in GCC is built-in rather than from library.

Some people can remark that actually in the 2 versions, 2nd version supersedes the 1st one. That means if I remove the 1st version and put it internally into the 2nd, then everything is alright. But I have a good reason to keep the 1st version: I want the calls with just a string to go directly without unneccessarily calling vsnprintf. So please do not suggest me this way.

I have also thought about combining the 1st version into the 2nd, and then internally count the number of given arguments to know how to go. But it doesn't seem to have a standard way to do that. Determining the number of arguments is possible with variadic templates but not with variadic functions. And if I switch into variadic template, I cannot use va_start anymore in GCC.

Hope someone can help!!

Upvotes: 0

Views: 210

Answers (1)

melpomene
melpomene

Reputation: 85837

I haven't tested this, but wouldn't the following work?

void query_varargs(const string &s, ...) {
    va_list vargs;
    va_start(vargs, s);
    // ...
}

template<typename... Values>
enable_if<(sizeof...(Values) > 0), void>::type
query(const string& s, Values value...) {
    query_varargs(s, ...value);
}

I.e. move the functionality into a different function (query_varargs), then have the variadic template version of query forward to it.

Upvotes: 1

Related Questions