[comp.lang.c] Prototyping Woes

kreed@telesys.cts.com (Kevin W. Reed) (03/05/91)

I have a routine that can accept multiple parameters.  I'm having a problem
getting the compiler to accept a prototype so that it doesn't complain
that I have "too many actual arguments".

The routine takes a string of characters with fields separated by |'s and
extracts the fields and places them into the arguments that it is called
with.

For example:

	buffer = "field1|field2|field3" 

		would be extracted with:

	pickit ( 3, buffer, fld1, fld2, fld3);

		and
			
	buffer = "field1|field2|field3|field4" 

		would be extracted with:

	pickit ( 4, buffer, fld1, fld2, fld3, fld4);
	
The routine works great.  But the compiler continues to complain
with warning errors that "too many actual parameters" are being called.

I have tried several variations, all with the same results.

Below is the routine, Any suggestions?

----- pickit routine ------

pickit(int nargs, char *buffer, char *args)
{
	char **p;
	int  i,	l;

	p = &args;
	l=0;

	/* First check that they are enough fields for the request */
	
	for ( i = strlen (buffer) ; i > 0 ; i-- )
		if ( buffer[ i - 1 ] == '|' )
			l++;

	if ( l < nargs )
		return(ERROR);

	/* Now extract the fields */
	
	for ( i = 0 ; i < nargs ; i++ )
		{
		while ( ( *( *p )++ = *buffer++ ) ! = '|' );
		*( *p - 1 ) = '\0';
		p++;
		}

	return(TRUE);
}

----- end of pickit routine ------

Below is the actual error message that I get from the compiler [only one
shown for brevity]

ts2umodify.c(122) : warning C4020: 'pickit' : too many actual parameters

The system I'm using is...

System: Intel 386DX Clone
OS:	SCO Xenix 386 2.3.[23]
Dev:	SCO Development System 2.3.1b

Thanks for the help

-- 
Kevin W. Reed --- TeleSys Development Systems -- PO 17821, San Diego, CA 92177
TeleSys-II BBS & telesys.UUCP 619-483-3890 ----- Telebit PEP Line 619 483 0965
UUCP: {nosc,ucsd}!crash!telesys!kreed -------- Internet: kreed@telesys.cts.com

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (03/11/91)

I haven't seen any followups to this, so I'm doing it.
In article <1991Mar5.030826.15713@telesys.cts.com>, kreed@telesys.cts.com (Kevin W. Reed) writes:
> I have a routine that can accept multiple parameters.
> I'm having a problem getting the compiler to accept a prototype
> so that it doesn't complain that I have "too many actual arguments".

An ANSI C prototype for a variadic function ***MUST*** have an ellipsis
"..." in it, and that must be the last thing in the prototype.  For
example, we can prototype printf() thus:
	int printf(char *, ...);

> pickit(int nargs, char *buffer, char *args)
>     {
> 	char **p;
> 	int  i,	l;
> 
> 	p = &args;
> 	l = 0;
> 
> 	/* First check that they are enough fields for the request */
			    ^^^^ "there"?
> 	
> 	for (i = strlen(buffer); i > 0; i--)
> 	    if (buffer[i-1] == '|')
> 		l++;
> 	if (l < nargs) return ERROR;
> 
> 	/* Now extract the fields */
> 	
> 	for (i = 0; i < nargs; i++) {
> 	    while (( *( *p )++ = *buffer++) ! = '|' );
> 	    *( *p - 1 ) = '\0';
> 	    p++;
> 	}
> 
> 	return TRUE;
> }

The function header here clearly and explicitly states that the
function has exactly three arguments, no more and no fewer.  There
is an implicit assumption here that pointer parameters are passed as
a contiguous block in ascending order with no gaps.  This assumption
simply isn't true on Pyramids, SPARCs, MIPS, and several other machines.
The code is not portable at all.

There are at least two ways to fix this.  One of them is to look up
"varargs" in an ANSI C manual or textbooks.  There are special macros
for variadic functions which MUST be used if you want portable code.

In this particular case, there isn't any real need to pass varying
numbers of arguments.  Pass *one* argument, an array of pointers.

	/*  split(string, delimiter, pointer_array, N)
	    is given a string, such as "foo|baz|ugh"
	    and a delimiter, such as '|'.
	    It parses the string, putting pointers to the fields
	    in the appropriate elements of pointer_array, and
	    replacing instances of the delimiter by NUL.
	    The result of the function is the number of fields
	    encountered.  If more than N are found, only the first
	    N fields will be stored and returned.  If fewer than
	    N are found, trailing fields are set to NULL (char*)0.
	    I have chosen to make "delim" an 'int' parameter so
	    that the function can be called without a prototype in
	    scope.  awk(1) programmers will recognise the function.
	    An easy generalisation would be to allow a set of delimiters.
	*/
	int split(char *source, int delim, char **fields, int N)
	    {
		register char *s;
		register int c;
		register int k;

		if (N <= 0) return 0;
		fields[0] = source;
		for (s = source, k = 1; (c = *s++) != '\0'; )
		    if (c == delim) {
			s[-1] = '\0';
			if (k == N) return k;
			fields[k++] = s;
		    }
		for (c = k; c < N; )
		    fields[c++] = (char*)0;
		return k;
	    }		

Now, to extract 3 fields from a string, we might do
	#define Max_String_Len /* whatever */
	#define N_fields 3
		char buffer[Max_String_Len+1];
		char *fields[N_fields];
		int field_count;

		field_count = split(
			strcpy(buffer, "field1*field2*field3"), '*',
			fields, N_fields);

It should be obvious how to adapt that to check for exactly N fields.
-- 
The purpose of advertising is to destroy the freedom of the market.