Monday, June 21, 2010

Understanding va_list

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
#include

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

No comments:

Post a Comment