Are there gotchas using varargs with reference parameters - c++

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.

Related

How does this method being called with CLASS macro work?

I recently received a request from an acquaintance to assist with building a C++ solution after their developer unfortunately passed away. I'm relatively new to C++ and don't quite understand what the following lines are doing.
This code is from a customized version of the dcraw.cpp library by Dave Coffin.
The MACRO is defined as
#define CLASS
The method being called looks like this
void CLASS merror (void *ptr, char *where)
{
if ( ptr ) return;
//fprintf (stderr,_("%s: Out of memory in %s\n"), ifname, where);
sprintf (PSstring(),"%s: Out of memory in %s\n", ifname, where);
PSputDiag ();
longjmp (failure, 1);
}
And the method call looks like this
merror (fimg, "wavelet_denoise()");
When trying to compile the code I receive about 1800 errors, 258 of which are similar to the following.
C2664 'void merror(void *,char *)': cannot convert argument 2 from 'const char [15]' to 'char *' Photoshoot5 C:\Users\source\projectname\MVDcraw.cpp 991
I've been searching for quite some time to figure out what is going on, but since I don't even know what the method call is doing I've been unable to find anything that is relevant.
Any help would be greatly appreciated.
The macro
#define CLASS
has nothing to do with your error. After the macro is expanded the function is:
void merror (void *ptr, char *where)
{
if ( ptr ) return;
//fprintf (stderr,_("%s: Out of memory in %s\n"), ifname, where);
sprintf (PSstring(),"%s: Out of memory in %s\n", ifname, where);
PSputDiag ();
longjmp (failure, 1);
}
That is, the macro is replaced by nothing.
The code appears to be written before C++11. From cppreference...
until C++11:
String literals are convertible and assignable to non-const char* or wchar_t* in order to be compatible with C, where string literals are of types char[N] and wchar_t[N]. Such implicit conversion is deprecated.
since C++11:
String literals are not convertible or assignable to non-const CharT*. An explicit cast (e.g. const_cast) must be used if such conversion is wanted.
Converting string literals to char* never was ok, but only since C++11 it is an error. If you are using C++17 you can use std::string::data():
std::string where{"wavelet_denoise()"};
merror (fimg, where.data());
Actually better would be to change the method, as suggested in a comment by Richard Critten, to take a const char* where.

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.

How to get all arguments from following function in c/c++?

following is the implementation of my method
static VALUE myMethod(VALUE self, VALUE exc, const char* fmt, ...) {
// Need to get all the arguments passed to this function and print it
}
function is called as follows:
myMethod(exception, ""Exception message: %s, Exception object %d",
"Hi from Exception", 100);
Can you provide the code for myMethod() that will access all the arguments and print them out.
Thanks in advance.
The va_start and va_arg macro's are used to get the variable arguments in a function.
An example can be found on the Microsoft site: http://msdn.microsoft.com/en-us/library/kb57fad8(v=vs.71).aspx
In your case it's a bit trickier, since you need to parse the format string to exactly know how many arguments should be given and of which type they are. Luckily, the CRT contains a function for that. The vfprintf function can be given a va_list (which you get from va_start). vfprintf will use this one to process all the extra arguments. See http://www.cplusplus.com/reference/clibrary/cstdio/vfprintf/ for an example.
One way is to use vsnprintf().
Sample code:
char buf[256];
va_list args;
va_start(args, fmt);
if(vsnprintf(buf, sizeof(buf), fmt, args) > 0)
fputs(buf, stderr);
va_end(args);
You need to use va_start and va_arg macros to get the arguments.
You can take a look at this - it has some examples.
http://www.go4expert.com/forums/showthread.php?t=17592

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;
}

va_arg returning the wrong argument

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.