Understanding va_list
Writing your own function that uses a va_list is really easy!
But first, let’s identify what a va_list is.
Think about the printf() C function.
printf(“Hello there! I like the numbers %d, %d and %d\n\n\n”, 1, 3, 7);
Obviously the output of that function call would be:
Hello there! I like the numbers 1, 3 and 7
But the key point here is, the printf() function can accept a VARYING NUMBER OF ARGUMENTS. That’s because it uses a va_list.
If you look at the signature for printf(), it looks like this:
int printf( char * format, … );
So the argument list for printf() has 2 main things:
- char * format – a regular string
- and a second special argument, … (3 dots, just like that)
… is called an “ellipsis”, and it means, in plain English: “any number of optional arguments can go here.”
So somehow, in the innermost bowels of printf(), is some sticky code that somehow retrieves each one of the the list of args you’re passing in, in the place of the “…”.
Cool! So is it possible for us to write our functions that have their own sticky code that can process a set of VARIABLE ARGUMENTS???
YES YOU CAN. And its actually simple!
An example:
#include <stdio.h>
#include <stdarg.h>
int addThemAll( int numargs, ... )
{
// So this function can accept a variable number
// of arguments. No (practically speaking) limits.
// RULES you must know in order to be able to use "..." in one of your
// own functions:
//
// 1) The ... MUST appear exactly as ...
// It cannot be "..." (with the quotes),
// '...', or anything else weird.
//
// 2) The ... __MUST GO LAST__ IN THE ARGUMENT LIST
//
// 3) THERE MUST BE AT LEAST ONE MANDATORY, NON-OPTIONAL ARGUMENT,
// THAT COMES BEFORE THE ...
// We'll be using these macros here:
/*
va_list va_start va_end va_arg
*/
// All of the above va_* things are actually special MACROS,
// exclusively defined for us to use when working with
// _V_ariable _A_rgument lists.
// FIRST, we create a POINTER that will be used
// to point to the first element of the VARIABLE
// ARGUMENT LIST.
va_list listPointer;
// Currently, listPointer is UNINITIALIZED, however,
// SO, now we make listPointer point to
// the first argument in the list
va_start( listPointer, numargs );
// Notice that numargs is the LAST MANDATORY ARGUMENT
// that the addThemAll() function takes.
// By "LAST MANDATORY ARGUMENT", I mean 'numargs'
// is the last argument to the addThemAll() function
// JUST BEFORE the "..."
// NEXT, we're going to start to actually retrieve
// the values from the va_list itself.
// THERE IS A CATCH HERE. YOU MUST KNOW THE
// DATA TYPE OF THE DATA YOU ARE RETRIEVING
// FROM THE va_list. In this example, I'm assuming
// they're all ints, but you could always pass a format
// string that lets you know the types.
int sum = 0;
for( int i = 0 ; i < numargs; i++ )
{
// GET an arg. YOU MUST KNOW
// THE TYPE OF THE ARG TO RETRIEVE
// IT FROM THE va_list.
int arg = va_arg( listPointer, int );
printf( " The %dth arg is %d\n", i, arg );
sum += arg;
}
printf("--");
printf("END OF ARGUMENT LIST\n\n");
// FINALLY, we clean up by saying
// va_end(). Don't forget to do this
// BEFORE the addThemAll() function returns!
va_end( listPointer );
printf("The total sum was %d\n\n", sum);
return sum;
}
int main()
{
// Try it out.
printf("Calling 'addThemAll( 3, 104, 29, 46 );' . . .\n");
addThemAll( 3, 104, 29, 46 );
printf("Calling 'addThemAll( 8, 1, 2, 3, 4, 5, 6, 7, 8 );' . . .\n");
addThemAll( 8, 1, 2, 3, 4, 5, 6, 7, 8 );
return 0;
}
16 Comments
OK. This is clear and simple. but what if I want to postpone processing the variable list for later? For example, say that I have a class A:
class A{
public:
void setParams( int args, …);
void processParams();
};
surly I need to keep the variable parameters in A somehow between the call to ’set’ and ‘process’. If you are looking for justification for such requirement, it is very simple: from one context you set the parameters and from another you process them.
put them into an array!
Please be more specific. what is the type of this array? How to declare:
myArray[ args ]; ?
What is ?
If I need to process the arguments to keep them, then it doesn’t help. I want to postpone the processing. I need something that looks like the stack when entering a function with ‘…’ parameters.
Any suggestions?
assuming, they’re all ints, you can maintain an array like:
int * arr = (int*)malloc( numargs*sizeof(int) );
for( int i = 0 ; i < numargs; i++ )
{
// . . get arg, store in array
}
careful of leaks!
hope that helps
Nope. If I know the types I have no problems. If it is only one type, even if I don’t know what is this type – still no problem (I can figure the type later and copy the parameters to the largest type acceptable array). I need the whole – different types, different number of variables, completely unseen. As it in printf. Actually, I need that my users will use my function exactly as they use printf. Only that the processing of %d, %p and alike will be done later, in a different task. I didn’t find a solution for this up till now.
OK. After writing you the answer the solution entered my mind. The idea is to keep every variable in the list in the *maximum* storage that might be needed. That means, for instance, that every parameter is copied into a 4 bytes length field. then, if I have this code: Ranf( “char %c, int %d” ); they will both be copied into an array of 4 bytes fields:
UINT32 myVariableTypes[ MAX_PARAMETERS ];
Ranf( const char* text, … )
{
//copy every parameter into //myVariableTypes. Later the string //’text’ will indicate the specific type
}
The only problem is how to know the number of parameters. For this I can either scan ‘text’ or ask the user to supply it: Ranf( UINT8 noOfParameters, const char* text, … );
It is a bit annoying but saves run time, which is very critical for me.
what do you say?
This is how I might do it.
I’ve preserved the original datatypes as well.
Hello,
your exemple is very clear and helpful.
But i’d like to know how can i change value of vars as scanf do?
Thank you for your example, it helped me to do my custom fprintf(UARTPutChar,”Hello World! %d”, year) function for example, that prints to UART output instead to stdin or string array.
Here is a ANSI C example of how pass … from one function to another without intermediate pointer. I believe that works in C++ too
#include
#include
void
my_funcv (const char *text, va_list args)
{
printf(“I’m my_newv and I receive … from my_func\n”);
printf(“Value is: %s\n”, text);
char *value;
while (value = va_arg(args, char *))
printf(“Value is: %s\n”, value);
}
void
my_func (const char *text, …)
{
va_list args;
va_start(args, text);
my_funcv (text, args);
va_end (args);
}
int
main (int argc, char *argv[])
{
my_func(“This”, “is”, “a”, “test”, NULL);
return 0;
}
Can you have a function that takes a variable argument list and directly pass it to another fucntion?
void myLogger(char *format, int numArgs, …)
{
systemLogger(char *format, int numArgs, …);
}
or do you have to extract the argument then pass them on to the second function?
void myLogger(cahr *format, int
You can pass listPointer to another function, yes
vsprintf is an example of a such function that accepts an already started va_list as an argument
void log( char * fmt, ... ) { // create and start the va_list va_list listPointer ; va_start( listPointer, fmt ) ; // vsprintf is an example of // a function that works with // an "already started" va_list static char buf[ 1024 ] ; vsprintf( buf, fmt, listPointer ) ; }Very good discussion. Another way to pass va_list is by using va_copy. This creates a memory copy of the listPointer on the heap. This macro needs to be within the va_start and va_end block so that the memory assigned can be released.
I am glsd to see the discussions here,
But i ahve a doubt here. How to find out the second argument directly , i know the type previously.
The condition is
a) some times i have only one argument a char * string
b) some time i have 2 arguments one a string and other an integer.
How i will find out the existance of second argument.
It will be very greatful i will get a proper reply. Thanks in advance.
Here’s another challange,
What if I need to pass a different number of arguments to a function (one of a set).
Consider the following code:
//declerations
void A::foo();
void A::foo(int n1);
void A::foo(int n1, int n2);
void A::foo(int n1, int n2, int n3);
void A::my_func(int num_args, …)
{
.
.
.
// now I need to call specific foo()
foo(???);
}
Do I have to parse the aguments before and then call the function? I mean with a set of ‘if’ statements [if (num_args==0) call this, if (num_args==1) call that and so on]
OR
Do I have a simple way?
Thanks.
It looks like the way you’re doing it is the simplest way.
void A::my_func(int num_args, ... ) { switch( num_args ) { case 0: foo() ; break; case 1: // pull 1 arg here, foo( arg ) ; break; } }I think you’re thinking of scripting languages like javascript where you can “form” the string of code you want to execute, then eval() it later. But in C++ at some point in the code you’re going to have to explicitly write the code that calls the 1 argument version, 2 argument version, etc