va_arg returning the wrong argument - c++

With the following code va_arg is returning garbage for the second and third pass through vProcessType.
// va_list_test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <tchar.h>
#include <cstdarg>
#include <windows.h>
void processList(LPTSTR str, ...);
void vProcessList(LPTSTR str, va_list args);
void vProcessType(va_list args, int type);
int _tmain(int argc, _TCHAR* argv[])
{
LPTSTR a = TEXT("foobar");
int b = 1234;
LPTSTR c = TEXT("hello world");
processList(TEXT("foobar"), a, b, c);
return 0;
}
void processList(LPTSTR str, ...)
{
va_list args;
va_start(args, str);
vProcessList(str, args);
va_end(args);
}
void vProcessList(LPTSTR str, va_list args)
{
vProcessType(args, 1);
vProcessType(args, 2);
vProcessType(args, 1);
}
void vProcessType(va_list args, int type)
{
switch(type)
{
case 1:
{
LPTSTR str = va_arg(args, LPTSTR);
printf("%s", str);
}
break;
case 2:
{
int num = va_arg(args, int);
printf("%d", num);
}
break;
default:
break;
}
}
Is passing a va_list thing not allowed in this way? The first call to va_arg inside vProcessType returns the expected string. The second and third time through this function it returns a pointer to the start of the first string, instead of the values expected.
If I hoist the va_arg call to vProcessList, everything seems to work fine. It only seems when I pass a va_list through a function that I'm getting this behaviour.

You're passing the same va_list each time to vProcessType() - in each call to vProcessType() you're acting on the first va_arg in the list.
So you're always dealing with the TEXT("foobar") parameter when calling vProcessType().
Also note that the standard has this to say about passing a va_list to another function:
The object ap [of type va_list] may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap, the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap.
A foot note in the standard indicate that it's perfect OK to pass a pointer to a va_list, so what you might want to do is have vProcessType() take a pointer to the va_list:
void vProcessType(va_list* pargs, int type);

When you pass the va_list object to another function and that another function uses va_arg on the corresponding parameter, that va_list will have indeterminate value in the calling function when the control returns. The only thing you are allowed to do is to apply va_end to that va_list object.
This is how it is stated in the standard (7.15/3)
If access to the varying arguments is
desired, the called function shall
declare an object (generally referred
to as ap in this subclause) having
type va_list. The object ap may be
passed as an argument to another
function; if that function invokes the
va_arg macro with parameter ap, the
value of ap in the calling function is
indeterminate and shall be passed to
the va_end macro prior to any further
reference to ap.
Don't pass va_listobjects by value. If your intent is to continue argument parsing in each consequent sub-function, then you have to pass the va_list object by pointer.
If you really want to pass your va_list object by value, i.e. if you want each sub-function to parse from the same point, you have to manually copy your va_list object in advance by using the va_copy macro. ("In advance" means that you have to make as many copies as you'll need before any sub-function has a chance to do a va_arg on it.)

You're passing your va_list by value. Try passing a pointer to the one value instead (or if you wanted a C++ only version, you could use a reference):
void vProcessList(LPTSTR str, va_list args)
{
vProcessType(&args, 1);
vProcessType(&args, 2);
vProcessType(&args, 1);
}
void vProcessType(va_list *args, int type)
{
...
LPTSTR str = va_arg(*args, LPTSTR);
...
int num = va_arg(*args, int);
}

As others have noted, the C99 standard allows a portable (among C99 implementations) way for a program to run through a va_list more than once. There isn't any nice portable way to do that in pre-C99 implementations. What I did when I needed to run through a printf list more than once (for a "center string" function which had to evaluate the arguments once to determine how wide they would be, and then a second time to actually display them) was to examine the compiler vendor's "stdarg.h" and fudge my own implementation of the necessary functionality.
If one wanted a really portable implementation that would work on earlier C compilers, and if one knew in advance the maximum number of passes that would be required, I think one could create an array of va_ptr objects, use va_start on all of them, and then pass the array to the child routine. Sorta icky, though.

Related

How to properly va_end?

#include <cstdarg>
using namespace std;
void do_something( va_list numbers, int count ) {
// ^
// Should I call this by reference here? I mean, va_list & numbers?
//... stuff
va_end( numbers );
}
void setList( int count, ... ) {
va_list numbers;
va_start( numbers, count );
do_something( numbers, count );
}
int main() {
setList( 2, 0, 1 );
return 0;
}
When taking va_list to another function, how should I pass it to that function? I know that va_end must be called when things are done with va_list I'm confused whether I should call it by reference or not. Will va_list be properly ended even though it is not called by reference?
Variable argument lists:
You should never use va_end in a function taking the va_list as argument!
From the (Linux) man-page for va_arg:
Each invocation of va_start() must be matched by a corresponding
invocation of va_end() in the same function.
(Emphasis mine)
In a function having a va_list argument, take the va_list by value (as C-library functions like vprintf, ... do, too).
Example:
#include <cstdarg>
#include <iostream>
void v_display_integers(int count, va_list ap) {
while(0 < count--) {
int i = va_arg(ap, int);
std::cout << i << '\n';
}
}
void display_integers(int count, ...) {
va_list ap;
va_start(ap, count);
v_display_integers(count, ap);
va_end(ap);
// You might use 'va_start' and 'va_end' a second time, here.
// See also 'va_copy'.
}
int main() {
display_integers(3, 0, 1, 2);
}
Note:
In C++ you should avoid variable argument lists. Alternatives are std::array, std::vector,std::initializer_list and variadic templates.
I prefer to call va_end in the same function in which I called va_start or va_copy, like any other resource allocation/deallocation pair. That style is required by the standard, although some implementations are more tolerant.
Normally, va_lists are passed by value. They are small objects.
The macro function va_end is implemented as either void statement or resetting the va_list variable.
http://research.microsoft.com/en-us/um/redmond/projects/invisible/include/stdarg.h.htm
Theoretically, you can call it anywhere, or even skip it. However, to be a good programmer, we need to put it after the va_ functions

vprintf not consuming item from va_list

I am trying to write a variation of printf where the item being printed and the format being printed are adjacent parameters in the call like...
print(2, "%s", "hello", "%.5u", 25);
I have studied up on var args and came up with....
void print(int count, ...)
{
va_list varg;
va_start(varg, count);
while(count-- > 0)
{
char* format = va_arg(varg, char*);
vprintf(format, varg);
}
va_end(varg);
}
It appears that vprintf is not consuming the item it is using from the stack. My output is
hellohello
I believe this is being expanded out too
printf("%s", "hello);
printf("hello");
So what am I doing wrong that vprintf isn't consuming the "hello" from the arg list?
Update: per the comment below
void print(int count, ...)
{
va_list varg;
va_start(varg, count);
while(count-- > 0)
{
char* format = va_arg(varg, char*);
void* arg = va_arg(varg, void*);
printf(format, arg);
}
va_end(varg);
}
This seems to get the job done.
The va_list is nothing but an address (in typical implementations). It is passed by value to the function, so the original in the calling function is unmodified.
What you're doing here is undefined behavior. In the C99 standard, section 7.15 paragraph 3, it says:
The type declared is va_list which is an object type suitable for
holding information needed by the macros va_start, va_arg,
va_end, and va_copy. If access to the varying arguments is
desired, the called function shall declare an object (generally
referred to as ap in this subclause) having type va_list. The
object ap may be passed as an argument to another function; if that
function invokes the va_arg macro with parameter ap, the value of
ap in the calling function is indeterminate and shall be passed to
the va_end macro prior to any further reference to ap.
Here, you're passing the va_list variable varg to the function vprintf, which calls va_arg on it internally. Thus, after it returns, you are not allowed to use varg anymore. You are required to call va_end on it before doing anything else with it. Instead, you are using it in the next iteration of the loop without first calling va_end. So you are running into undefined behavior.

Is it OK to recursively parse the args in a va_list?

Suppose I want to make a function that recursively parses a variadic argument list, by letting each invocation of the function read the next argument? After handing the va_list to the next function, I am not intending to continue using the va_list in the calling function. Is the following code ok:
void VarArgRecursive( va_list args ) {
int nextArg = va_arg(args, int);
if( nextArg != -1 ) {
printf("Next arg %d\n", nextArg);
VarArgRecursive(args);
}
}
void VarArgFunc( int firstArg, ... ) {
va_list args;
va_start(args, firstArg);
VarArgRecursive(args);
va_end(args);
}
int main (int argc, char * const argv[]) {
VarArgFunc(20, 12, 13, -1);
return 0;
}
The code compiles on my system, and the output is as expected:
Next arg 12
Next arg 13
So, is this practice OK? I have searched the list, and found that after handing the va_list over to the next function, the contents of the va_list in the calling function is undefined. That shouldn't matter for my usage, as I will not continue using the va_list after handing it over to the next (well, actually, the same) function. I have also checked this page:
http://c-faq.com/varargs/handoff.html
...which shows that my way of handing over the va_list to the next function is OK. What it doesn't say, is whether it is OK to hand the va_list over to yet another function after reading one arg, and expect the called function to read the next arg. If there are c++ -specific answers to this question, that is also ok, since it will be used in a c++ program.
You can pass it however many times you like, but you cannot "use" the va_list more than once. When consumed, the va_list may be modified and using it again is undefined behavior per the C++ spec.
If you want to use it more than once, call va_copy to clone the va_list prior to consuming it, then pass the copy.
However, in your case what you're doing is acceptable. The problem arises when you attempt to pass the va_list from the beginning to another function.

Ellipsis and va_args trick needed

TraceMessage is an WinAPI function with variable number of arguments. It is a tracing function, with a notation similar to printf, which generates a trace message in Windows tracing. The weird part here is that it receive a format string as part of the ellipsis, not as a dedicated argument.
It is possible to 'override' this function with a function of my own, which then needs to call TraceMessageVa (which is the same as TraceMessage, just with va_args rather than ellipsis).
So far so good; but now I want to access the traced message using a sprintf-like function, which has the format string out of the ellipsis. Thus I need to
- get the format string argument out of the ellipsis ;
- create a new va_list without the first argument.
Any idea about to how do it? Solutions specific to Visual Studio compiler are also acceptable. Thanks!
With a va_list you can pass it to a function which takes a va_list after having used va_arg on it already to have extracted one or more arguments. The va_list will then act like it "contains" only the rest of the arguments.
I have no experience with TraceMessage itself, but I've given an example using standard vprintf and a test function. You should be able to adapt as appropriate.
E.g.
#include <stdio.h>
#include <stdarg.h>
void test(int a, ...)
{
va_list va;
const char* x;
va_start(va, a);
x = va_arg(va, const char*);
vprintf(x, va);
va_end(va);
}
int main(void)
{
test(5, "%d\n", 6);
return 0;
}

Are there gotchas using varargs with reference parameters

I have this piece of code (summarized)...
AnsiString working(AnsiString format,...)
{
va_list argptr;
AnsiString buff;
va_start(argptr, format);
buff.vprintf(format.c_str(), argptr);
va_end(argptr);
return buff;
}
And, on the basis that pass by reference is preferred where possible, I changed it thusly.
AnsiString broken(const AnsiString &format,...)
{
... the rest, totally identical ...
}
My calling code is like this:-
AnsiString s1, s2;
s1 = working("Hello %s", "World");
s2 = broken("Hello %s", "World");
But, s1 contains "Hello World", while s2 has "Hello (null)". I think this is due to the way va_start works, but I'm not exactly sure what's going on.
If you look at what va_start expands out to, you'll see what's happening:
va_start(argptr, format);
becomes (roughly)
argptr = (va_list) (&format+1);
If format is a value-type, it gets placed on the stack right before all the variadic arguments. If format is a reference type, only the address gets placed on the stack. When you take the address of the reference variable, you get the address or the original variable (in this case of a temporary AnsiString created before calling Broken), not the address of the argument.
If you don't want to pass around full classes, your options are to either pass by pointer, or put in a dummy argument:
AnsiString working_ptr(const AnsiString *format,...)
{
ASSERT(format != NULL);
va_list argptr;
AnsiString buff;
va_start(argptr, format);
buff.vprintf(format->c_str(), argptr);
va_end(argptr);
return buff;
}
...
AnsiString format = "Hello %s";
s1 = working_ptr(&format, "World");
or
AnsiString working_dummy(const AnsiString &format, int dummy, ...)
{
va_list argptr;
AnsiString buff;
va_start(argptr, dummy);
buff.vprintf(format.c_str(), argptr);
va_end(argptr);
return buff;
}
...
s1 = working_dummy("Hello %s", 0, "World");
Here's what the C++ standard (18.7 - Other runtime support) says about va_start() (emphasis mine) :
The restrictions that ISO C places on
the second parameter to the
va_start() macro in header
<stdarg.h> are different in this
International Standard. The parameter
parmN is the identifier of the
rightmost parameter in the variable
parameter list of the function
definition (the one just before the
...).
If the parameter parmN is declared with a function, array, or reference
type, or with a type that is not
compatible with the type that results
when passing an argument for which
there is no parameter, the behavior
is undefined.
As others have mentioned, using varargs in C++ is dangerous if you use it with non-straight-C items (and possibly even in other ways).
That said - I still use printf() all the time...
A good analysis why you don't want this is found in N0695
According to C++ Coding Standards (Sutter, Alexandrescu):
varargs should never be used with C++:
They are not type safe and have UNDEFINED behavior for objects of class type, which is likely causing your problem.
Here's my easy workaround (compiled with Visual C++ 2010):
void not_broken(const string& format,...)
{
va_list argptr;
_asm {
lea eax, [format];
add eax, 4;
mov [argptr], eax;
}
vprintf(format.c_str(), argptr);
}
Side note:
The behavior for class types as varargs arguments may be undefined, but it's consistent in my experience. The compiler pushes sizeof(class) of the class's memory onto the stack. Ie, in pseudo-code:
alloca(sizeof(class));
memcpy(stack, &instance, sizeof(class);
For a really interesting example of this being utilized in a very creative way, notice that you can pass a CString instance in place of a LPCTSTR to a varargs function directly, and it works, and there's no casting involved. I leave it as an exercise to the reader to figure out how they made that work.