#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
Related
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.
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.
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.
I have a class that holds an "error" function that will format some text. I want to accept a variable number of arguments and then format them using printf.
Example:
class MyClass
{
public:
void Error(const char* format, ...);
};
The Error method should take in the parameters, call printf/sprintf to format it and then do something with it. I don't want to write all the formatting myself so it makes sense to try and figure out how to use the existing formatting.
Use vfprintf, like so:
void Error(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
vfprintf(stderr, format, argptr);
va_end(argptr);
}
This outputs the results to stderr. If you want to save the output in a string instead of displaying it use vsnprintf. (Avoid using vsprintf: it is susceptible to buffer overflows as it doesn't know the size of the output buffer.)
have a look at vsnprintf as this will do what ya want http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/
you will have to init the va_list arg array first, then call it.
Example from that link:
/* vsprintf example */
#include <stdio.h>
#include <stdarg.h>
void Error (char * format, ...)
{
char buffer[256];
va_list args;
va_start (args, format);
vsnprintf (buffer, 255, format, args);
//do something with the error
va_end (args);
}
I should have read more on existing questions in stack overflow.
C++ Passing Variable Number of Arguments is a similar question. Mike F has the following explanation:
There's no way of calling (eg) printf
without knowing how many arguments
you're passing to it, unless you want
to get into naughty and non-portable
tricks.
The generally used solution is to
always provide an alternate form of
vararg functions, so printf has
vprintf which takes a va_list in place
of the .... The ... versions are just
wrappers around the va_list versions.
This is exactly what I was looking for. I performed a test implementation like this:
void Error(const char* format, ...)
{
char dest[1024 * 16];
va_list argptr;
va_start(argptr, format);
vsprintf(dest, format, argptr);
va_end(argptr);
printf(dest);
}
You are looking for variadic functions. printf() and sprintf() are variadic functions - they can accept a variable number of arguments.
This entails basically these steps:
The first parameter must give some indication of the number of parameters that follow. So in printf(), the "format" parameter gives this indication - if you have 5 format specifiers, then it will look for 5 more arguments (for a total of 6 arguments.) The first argument could be an integer (eg "myfunction(3, a, b, c)" where "3" signifies "3 arguments)
Then loop through and retrieve each successive argument, using the va_start() etc. functions.
There are plenty of tutorials on how to do this - good luck!
Simple example below. Note you should pass in a larger buffer, and test to see if the buffer was large enough or not
void Log(LPCWSTR pFormat, ...)
{
va_list pArg;
va_start(pArg, pFormat);
char buf[1000];
int len = _vsntprintf(buf, 1000, pFormat, pArg);
va_end(pArg);
//do something with buf
}
Using functions with the ellipses is not very safe. If performance is not critical for log function consider using operator overloading as in boost::format. You could write something like this:
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;
class formatted_log_t {
public:
formatted_log_t(const char* msg ) : fmt(msg) {}
~formatted_log_t() { cout << fmt << endl; }
template <typename T>
formatted_log_t& operator %(T value) {
fmt % value;
return *this;
}
protected:
boost::format fmt;
};
formatted_log_t log(const char* msg) { return formatted_log_t( msg ); }
// use
int main ()
{
log("hello %s in %d-th time") % "world" % 10000000;
return 0;
}
The following sample demonstrates possible errors with ellipses:
int x = SOME_VALUE;
double y = SOME_MORE_VALUE;
printf( "some var = %f, other one %f", y, x ); // no errors at compile time, but error at runtime. compiler do not know types you wanted
log( "some var = %f, other one %f" ) % y % x; // no errors. %f only for compatibility. you could write %1% instead.
Have a look at the example http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/, they pass the number of arguments to the method but you can ommit that and modify the code appropriately (see the example).
A previous question showed a nice way of printing to a string. The answer involved va_copy:
std::string format (const char *fmt, ...);
{
va_list ap;
va_start (ap, fmt);
std::string buf = vformat (fmt, ap);
va_end (ap);
return buf;
}
std::string vformat (const char *fmt, va_list ap)
{
// Allocate a buffer on the stack that's big enough for us almost
// all the time.
s ize_t size = 1024;
char buf[size];
// Try to vsnprintf into our buffer.
va_list apcopy;
va_copy (apcopy, ap);
int needed = vsnprintf (&buf[0], size, fmt, ap);
if (needed <= size) {
// It fit fine the first time, we're done.
return std::string (&buf[0]);
} else {
// vsnprintf reported that it wanted to write more characters
// than we allotted. So do a malloc of the right size and try again.
// This doesn't happen very often if we chose our initial size
// well.
std::vector <char> buf;
size = needed;
buf.resize (size);
needed = vsnprintf (&buf[0], size, fmt, apcopy);
return std::string (&buf[0]);
}
}
The problem I'm having is that the above code doesn't port to Visual C++ because it doesn't provide va_copy (or even __va_copy). So, does anyone know how to safely port the above code? Presumably, I need to do a va_copy copy because vsnprintf destructively modifies the passed va_list.
You should be able to get away with just doing a regular assignment:
va_list apcopy = ap;
It's technically non-portable and undefined behavior, but it will work with most compilers and architectures. In the x86 calling convention, va_lists are just pointers into the stack and are safe to copy.
For Windows, you can simply define va_copy yourself:
#define va_copy(dest, src) (dest = src)
One thing you can do is if you do not otherwise need the vformat() function, move its implementation into the format() function (untested):
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <string>
#include <vector>
std::string format(const char *fmt, ...)
{
va_list ap;
enum {size = 1024};
// if you want a buffer on the stack for the 99% of the time case
// for efficiency or whatever), I suggest something like
// STLSoft's auto_buffer<> template.
//
// http://www.synesis.com.au/software/stlsoft/doc-1.9/classstlsoft_1_1auto__buffer.html
//
std::vector<char> buf( size);
//
// where you get a proper vsnprintf() for MSVC is another problem
// maybe look at http://www.jhweiss.de/software/snprintf.html
//
// note that vsnprintf() might use the passed ap with the
// va_arg() macro. This would invalidate ap here, so we
// we va_end() it here, and have to redo the va_start()
// if we want to use it again. From the C standard:
//
// 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.
//
// Thanks to Rob Kennedy for pointing that out.
//
va_start (ap, fmt);
int needed = vsnprintf (&buf[0], buf.size(), fmt, ap);
va_end( ap);
if (needed >= size) {
// vsnprintf reported that it wanted to write more characters
// than we allotted. So do a malloc of the right size and try again.
// This doesn't happen very often if we chose our initial size
// well.
buf.resize( needed + 1);
va_start (ap, fmt);
needed = vsnprintf (&buf[0], buf.size(), fmt, ap);
va_end( ap);
assert( needed < buf.size());
}
return std::string( &buf[0]);
}
va_copy() is directly supported starting in Visual Studio 2013. So if you can rely on that being available, you don't need to do anything.