Q:  How do you create and use a procedure that has a variable number of arguments?

A:   The ANSI edition of The C Programming Language by Kernighan and Ritchie has a fairly good explanation.  But there are a couple of things to note.

When you specify the type of the arg in the va_arg macro, you must specify the promoted type.  Therefore, since a char is always promoted to int in a variable argument list, it is never correct to use the type char in a va_arg macro.  Similarly, you should never use short or float as they are promoted to int or double respectively.  You should always used int, double, or a pointer type.

It is not possible for the called function to ask the run-time system how many arguments were passed.  So, the function must be able to determine the number and types of all arguments from the fixed arguments.  For example, printf can tell how many arguments to print based on the control string that is passed in.   You can implement your function using this paradigm, or an EOF marker, or possibly a number denoting the number of arguments to follow.

Following is a simple example that shows how everything interacts:

#import <stdio.h>
#import <stdarg.h> // for ANSII

extern void varArgs(int type, ...);
extern void doVariable(va_list ap);

#define PRINTF_TYPE	1
#define MY_TYPE		0

#define MY_CHAR		'c'
#define MY_DIGIT	'd'
#define MY_STR		's'
#define MY_EOF		'\0'

void doVariable(va_list ap)
{
	char pType;

	while ((pType = (char)va_arg(ap, int)) != MY_EOF) {
		switch(pType) {
		    case MY_CHAR:
		    default:
			/* note the promotion to int */
			printf("%c", va_arg(ap, int));
			break;
		    case MY_DIGIT:
			printf("%d", va_arg(ap, int));
			break;
		    case MY_STR:
			printf("%s", va_arg(ap, char *));
			break;
		}
	}
	printf("\n");
}

void varArgs(int type, ...)
{
	va_list ap;
		
	va_start(ap, type);	/* initialize the argument pointer */
	if (type == MY_TYPE) {
		doVariable(ap);
	}
	else {			/* this case is here to show how you can use vprintf. */
		char *fmt = va_arg(ap, char *);
		vprintf(fmt, ap);
	}
	va_end(ap);		/* clean up */
}

main()
{
	/* you don't need MY_EOF for PRINTF_TYPE msgs because the format string
	 * lets vprintf know how many args to expect.
	 *
	 * A better way to do the MY_TYPE would be to have a format string (like printf)	 */
	varArgs(PRINTF_TYPE, "this is msg #%d for %s\n", 1, "me" );

	varArgs(MY_TYPE, MY_CHAR, '1', MY_STR, "one", MY_DIGIT, 1, MY_EOF);
	varArgs(MY_TYPE, MY_CHAR, '1', MY_CHAR, '2', MY_CHAR, '3', MY_EOF);
	varArgs(MY_TYPE, MY_STR, "3", MY_STR, "three", MY_STR, "3", MY_EOF);
	varArgs(MY_TYPE, MY_DIGIT, 4, MY_DIGIT, 5, MY_DIGIT, 6, MY_EOF);

	varArgs(PRINTF_TYPE, "this is msg #%d for %s\n", 2, "you" );
}

Here is how you would implement it with methods:

	/* method declaration */
	- varArgs:(int)type,...;

	/* somewhere in the code... */	
	- varArgs:(int)type,...
	{
		va_list parameter_list;
	    
		va_start(parameter_list, type);
		
	    	/* do some stuff ... */
						
		va_end(parameter_list);
	}
	
	/* Here's how you call it */
	main()
	{
		[someObject varArgs:MY_TYPE, MY_DIGIT, 4, MY_EOF];
	}
	
QA627

Valid for 2.0, 3.0