HPlogo HP C/HP-UX Reference Manual: Version A.05.55.02 > Chapter 10 HP C/HP-UX Implementation Topics

The varargs Macros

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Index

The varargs macros allow accessing arguments of functions where the number and types of the arguments can vary from call to call.

NOTE: The <varargs.h> header has been superseded by the standard header <stdarg.h>, which provides all the functionality of the varargs macros. The <varargs.h> header is retained for compatibility with pre-ANSI compilers and earlier releases of HP C/HP-UX.

To use varargs, a program must include the header <varargs.h>. A function that expects a variable number of arguments must declare the first variable argument as va_alist in the function declaration. The macro va_dcl must be used in the parameter declaration.

A local variable should be declared of type va_list. This variable is used to point to the next argument in the variable argument list.

The va_start macro is used to initialize the argument pointer to the initial variable argument.

Each variable argument is accessed by calling the va_arg macro. This macro returns the value of the next argument, assuming it is of the specified type, and updates the argument pointer to point to the next argument.

The va_end macro is provided for consistency with other implementations; it performs no function on the HP 9000 Series 800 computers. The following example demonstrates the use of the <varargs.h> header:

Example

#include <varargs.h>
#include <stdio.h>

enum arglisttype {NO_VAR_LIST, VAR_LIST_PRESENT};
enum argtype {END_OF_LIST, CHAR, DOUB, INT, PINT};

int foo (va_alist)
va_dcl /* Note: no semicolon */
{
     va_list ap;
     int a1;
     enum arglisttype a2;

     enum argtype ptype;
     int i, *p;
     char c;
     double d;

     /* Initialize the varargs mechanism */
     va_start(ap);

     /* Get the first argument, and arg list flag */
     a1 = va_arg (ap, int);
     a2 = va_arg (ap, enum arglisttype);

     printf ("arg count = %d\n", a1);

     if (a2 == VAR_LIST_PRESENT) {
  /* pick up all the arguments */
  do {
      /* get the type of the argument */
      ptype = va_arg (ap, enum argtype);

      /* retrieve the argument based on the type */
      switch (ptype) {
   case CHAR:  c = va_arg (ap, char);
        printf ("char = %c\n", c);
        break;

   case DOUB:  d = va_arg (ap, double);
        printf ("double = %f\n", d);
        break;

   case PINT:  p = va_arg (ap, int *);
        printf ("pointer = %x\n", p);
        break;

   case INT :  i = va_arg (ap, int);
        printf ("int = %d\n", i);
        break;

   case END_OF_LIST :
        break;

   default:    printf ("bad argument type %d\n", ptype);
        ptype = END_OF_LIST; /* to break loop */
        break;
      } /* switch */
  } while (ptype != END_OF_LIST);
     }

     /* Clean up */
     va_end (ap);
 }

 main()
 {
     int x = 99;

     foo (1, NO_VAR_LIST);
     foo (2, VAR_LIST_PRESENT, DOUB, 3.0, PINT, &x, END_OF_LIST);
 }

C9X standard macros

The C9X standard-compliant version of "variadic" or variable argument (varargs) macro notation has been added to the HP ANSI C compiler. The notation for C9X standard and for the GNU version of varargs is very similar.

If you have coded your macros to the GNU standards, you can expect GNU-style behavior using the HP ANSI C compiler. If you have coded your macros to C9X standards, you can expect C9X-style behavior.

Usage differences

In the HP ANSI C compiler, an ellipsis (...) at the end of the parameter list in a macro definition indicates that the macro takes a variable number of arguments, or is variadic in nature. The ellipsis should be the final parameter in C9X style and should immediately follow the final parameter in GNU style.

The last parameter in a variable argument definition is referred to as the variable argument parameter. In GNU terminology, this is known as the "rest" parameter. In a C9X style definition, the variable argument parameter does not explicitly appear in the parameter list but is referred to by the identifier __VA_ARGS__ in its replacement text.

In the use of a variable arguments macro, a one-to-one correspondence is set up between the arguments in use and those in its definition. This is up to, but not including, the variable argument parameter. The rest of the arguments in the use of the macro definition are referred to as the trailing arguments. For purposes of expanding the macro, the entire combination of the trailing arguments (including the separating commas) is substituted for the variable argument parameter in the replacement text.

There are minor usage differences between how C9X and GNU specify variable argument macros are defined:

Table 10-3 How C9X and GNU define a variable argument macro

C9X

#define foo(f, ...) printf (f, __VA_ARGS__)
GNU

#define foo(f, s...) printf(f, s)

 

  • In the GNU style, the name of the variable parameter s precedes the ellipsis in the parameter list.

  • In the C9X standard, the identifier __VA_ARGS__ refers to the variable arguments.

  • The identifier __VA_ARGS__ can only occur only in the replacement-list of a function-like macro that uses the ellipsis notation in the arguments.

Variable names

Variable names are also handled slightly different by C9X and GNU.

Table 10-4 How C9X and GNU refer to varargs in macro replacement text

C9XGNU
Specified by the identifier __VA_ARGS__.Name appears in the replacement text.

 

For example:

  • __VA_ARGS__ in the replacement text indicates the variable name in the following C9X-style code:

    printf(f, __VA_ARGS__)

  • s in the replacement text indicates the variable name in the following GNU-style code:

    printf(f, s)

How HP C implements GNU and C9X macros

If you intend to use GNU style variable argument macros in HP C, note that you can make the concatenation operator '##' prevent syntax errors from occurring when the variable argument comes in as empty (the null string). However, you can also insert whitespace to the left of the left operand of '##' to more accurately specify the intended left operand.

For example, if you use

#define foo(f, s...) printf(f, s)

Then the macro "call"

foo("Hello world.\n");

results in the expansion

printf("Hello world.\n",);

(note the comma ",") causing a syntax error.

GNU provides the following workaround for this kind of a situation. If you use:

#define foo(f, s...) printf(f, ## s)

If the variable parameter 's' is non-null, if for example, you use:

foo("%s %d\n", "Cycles", "1024");

the result is

printf("%s %d\n", "Cycles", "1024");

as the expansion as you would expect.

However, if 's' is null, this erases the comma to the left of the '##' in the macro definition and resulting expansion is:

printf("Hello world.\n");

Note that the comma is gone.

In order to get the same behavior in HP C, you must insert a space to the left of the comma to make it clear to the preprocessor that the comma is the left operand of the '##' operator. Thus your definition for the macro 'foo' is:

#define foo(f, s...) printf(f , ## s)

(Note the space to the left of the '##' operator in the macro definition.)

If the space is not inserted, the left operand of the '##' operator is understood to be:

printf(f,

Because there is no parameter by that name for 'foo', it is erased.

© Hewlett-Packard Development Company, L.P.