Trying to understand how function overloading interacts with va_list

D

Dan Katz

Hi -

I'm trying to understand how function overloading interacts with
variable argument lists in C++, and I'm not getting much clarity.

In detail, I have some code which tries to overload a function with a
variable argument list:

------------------------------------------------------------------------
#include <iostream>
#include <stdarg.h> //vsprintf, va_list, va_start, va_end

using std::cout;
using std::endl;


void fun1(const char* extra, const char* format, va_list ap) {
char outString[100];
vsprintf(outString, format, ap);
cout << extra << outString << endl;
}

void fun1(const char* format, ...) {
va_list ap;
va_start(ap, format);
fun1("Extra: ", format, ap);
va_end(ap);
}

int main()
{
const char* format1 = "The format string with 1 param -- %s";
const char* format2 = "The format string with 2 params -- %s and %s";
const char* format3 = "The format string with 3 params -- %s and %s and %s";
const char* arg1 = "arg1";
const char* arg2 = "arg2";
const char* arg3 = "arg3";
fun1(format1, arg1);
fun1(format2, arg1, arg2);
fun1(format2, "arg1", "arg2");
fun1(format3, arg1, arg2, arg3);

return 0;
}
------------------------------------------------------------------------

and when I run this program I get the following:

------------------------------------------------------------------------
$ g++ --version
g++ (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)
[... copyright stuff elided ...]

$ g++ -Wall -W -ansi val_list_test_overload.cc
val_list_test_overload.cc: In function 'int main()':
val_list_test_overload.cc:32: warning: deprecated conversion from string constant to 'char*'

$ ./a.out
Extra: The format string with 1 param -- arg1
Extra: The format string with 2 params -- arg1 and arg2
The format string with 2 params -- %s and %sarg1
Extra: The format string with 3 params -- arg1 and arg2 and arg3
------------------------------------------------------------------------


I guess my questions are:

1) It appears that g++ considers the va_list type to be a (non-const)
char*. Is this normal? Is it standards compliant? (I didn't see
anything in the standard that would require it, but I am not a
language lawyer...)

2) It seems that the compiler is treating string literals differently
from variables which have been defined to be of type "const char*" --
i.e. in the two invocations of fun1 with the format2 string. In
particular, it seems to be doing an implicit conversion from string
constant to char*, but not from declared "const char*" to char*. But
I was under the impression that a string literal is in fact a "const
char*". Is there something in the standard that allows them to be
treated differently in this context?

3) Assuming that g++ is being standards compliant, is can anyone suggest
a better (portable, standards compliant) way to ensure that
fun1(format2, "arg1", "arg2")
ends up calling the
void fun1(const char* format, ...)
version of fun1?

Thanks!

Dan
 
M

Marcel Müller

Dan said:
void fun1(const char* extra, const char* format, va_list ap) {
char outString[100];
vsprintf(outString, format, ap);
cout << extra << outString << endl;
}

void fun1(const char* format, ...) {
va_list ap;
va_start(ap, format);
fun1("Extra: ", format, ap);
va_end(ap);
}

Don't do that for anything else than testing. The two overloads have
different semantics.
1) It appears that g++ considers the va_list type to be a (non-const)
char*. Is this normal? Is it standards compliant? (I didn't see
anything in the standard that would require it, but I am not a
language lawyer...)

As far as I know the standard says exactly nothing about the type of
va_list. But char* is common.

2) It seems that the compiler is treating string literals differently
from variables which have been defined to be of type "const char*" --
i.e. in the two invocations of fun1 with the format2 string. In
particular, it seems to be doing an implicit conversion from string
constant to char*, but not from declared "const char*" to char*. But
I was under the impression that a string literal is in fact a "const
char*". Is there something in the standard that allows them to be
treated differently in this context?

It is a C compatibility issue. Many older C code does not provide const
correctness. In C it was allowed to apply modifications to string
literals unless they are explicitly declared const.

3) Assuming that g++ is being standards compliant, is can anyone suggest
a better (portable, standards compliant) way to ensure that
fun1(format2, "arg1", "arg2")
ends up calling the
void fun1(const char* format, ...)
version of fun1?

Do not overload a function which takes a variable number of arguments
with a function that does not differ in the non-variable part of the
argument list. Simply don't do that.


Marcel
 
J

James Kanze

1) It appears that g++ considers the va_list type to be a (non-const)
char*. Is this normal? Is it standards compliant? (I didn't see
anything in the standard that would require it, but I am not a
language lawyer...)

All the standard says is that va_list is a typedef. To what is
implementation defined (or unspecified, I'm not sure). From a
QoI point of view, I'd expect some sort of special, unnamable
built-in type, but char* seems to be quite common.
2) It seems that the compiler is treating string literals differently
from variables which have been defined to be of type "const char*" --
i.e. in the two invocations of fun1 with the format2 string. In
particular, it seems to be doing an implicit conversion from string
constant to char*, but not from declared "const char*" to char*. But
I was under the impression that a string literal is in fact a "const
char*". Is there something in the standard that allows them to be
treated differently in this context?

Yes. A compatibility hack, dating back from the says of C (and
to a time when there wasn't const). Good compilers warn (like
g++ did here).
3) Assuming that g++ is being standards compliant, is can anyone suggest
a better (portable, standards compliant) way to ensure that
fun1(format2, "arg1", "arg2")
ends up calling the
void fun1(const char* format, ...)
version of fun1?

Yes. Give the functions different names. In general, if two
functions have different semantics, they should have different
names. Use overloading only when it doesn't really matter which
function is called (in terms of behavior).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top