[comp.lang.c] Variable number of arguments

richwill@swatsun.uucp (Rich Williamson) (02/19/88)

I'm trying to put together a simple formatted output routine for 
X-Windows. I would like to be able to say something like this

  MyXText( X, Y, formatString, arg1, ... , argn );

void
MyXText( X, Y, formatString, arg1, .... , argn )
int X;
int Y;
char *formatString;
??;
{
  char TempString[100];

  sprintf( TempString, formatString, arg1, ... , argn );

  X-windows stuff here.
}

I would like to pass the necessary parameters to sprintf but I
don't know how to declare the arguments to MyXText.  I don't know
how to declare a variable number of arguments.  Is it even possible?
The family of printf() functions seem to be able to receive variable
arguments, so it would seem to possible.

K&R aren't much help.  They say that it can be done but the resulting
code isn't portable. (p. 71)  Do they mean that you have to pop
the arguments off the stack by hand?  I don't need to do that, I
just want to pass the arguments transparently onto sprintf().

Thanks in advance for any helpful 'pointers'.

                               -- Richard Williamson

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
UUCP: seismo!bpa!swatsun!richwill
CSNET: richwill%swatsun.swarthmore.edu@relay.cs.net
-- 
UUCP: seismo!bpa!swatsun!richwill
CSNET: richwill%swatsun.swarthmore.edu@relay.cs.net

schwartz@gondor.cs.psu.edu (Scott E. Schwartz) (02/20/88)

In article <1608@byzantium.UUCP> richwill@byzantium.UUCP (Rich Williamson) writes:
>I'm trying to put together a simple formatted output routine for 
>X-Windows. I would like to be able to say something like this

>  MyXText( X, Y, formatString, arg1, ... , argn );

>I would like to pass the necessary parameters to sprintf but I
>don't know how to declare the arguments to MyXText.  I don't know
>how to declare a variable number of arguments.  Is it even possible?
>The family of printf() functions seem to be able to receive variable
>arguments, so it would seem to possible.

Hi Rich, how's life back at Swat?   Anyway, you should look at the
man page for varargs.   Varargs is the only portable way to pass
a varying number of arguments to a procedure.  Since you want to
use sprintf inside of MyXText, and since you are using a Sun with
System V compatible libraries, you should strongly consider using
vsprintf(), which directly takes the (varargs definded) parameters
to MyXText, saving you from having to unpack arg1...argn and pass
them to sprintf().



-- Scott Schwartz            schwartz@gondor.cs.psu.edu

tim@amdcad.AMD.COM (Tim Olson) (02/20/88)

In article <1608@byzantium.UUCP> richwill@byzantium.UUCP (Rich Williamson) writes:
| I would like to pass the necessary parameters to sprintf but I
| don't know how to declare the arguments to MyXText.  I don't know
| how to declare a variable number of arguments.  Is it even possible?
| The family of printf() functions seem to be able to receive variable
| arguments, so it would seem to possible.

Some C libraries supply the vprintf, vfprintf, and vsprintf routines. 
These use the varargs facility to print a variable number of parameters.

	-- Tim Olson
	Advanced Micro Devices
	(tim@amdcad.amd.com)

hst@mhres.mh.nl (Klaas Hemstra) (02/23/88)

Try using the vsprintf() function together with varargs.
Example:

   MyXText( X, Y, formatString, va_alist)
   ....
   va_dcl
   {   char 	TempString[100];
       va_list 	args;
       
       va_start(args);
       vsprintf(TempString , formatString, args);

       va_end(args);

   }

Note that you have to include the varargs.h file. This file is present in most
Unix systems and also with most non-Unix C compilers.

Be carefull using varargs !! Recently I had some trouble using them. 
If you think they are REALLY flexible you are wrong.
e.g.: You have a function accepting a variable number of arguments with
the va_alist declaration. Then you can call this function with (..,a1,a2,...).
But if you want to call the function from another function which also accepts
a variable number of arguments, you can NOT pass that variable number of
arguments to the first function mentioned. This is probably the reason
why vprintf,vfprintf & vsprintf exist.

Note that on some of the Unix systems I tried this was possible but not on all
of them ! (I don't want a discussion on where you can and where you can't use
this way of varargs passing).

friedl@vsi.UUCP (Stephen J. Friedl) (02/26/88)

In article <1632@mhres.mh.nl>, hst@mhres.mh.nl (Klaas Hemstra) writes:
> Try using the vsprintf() function together with varargs.
> Example:
> 
>    MyXText( X, Y, formatString, va_alist)
>    ....
>    va_dcl
>    {   char 	TempString[100];
>        va_list 	args;
>        
>        va_start(args);
>        vsprintf(TempString , formatString, args);
> 
>        va_end(args);
> 
>    }

     I think you are better with:

	MyXText(va_alist)
	va_dcl
	{
	char	buf[256];	/* buffer to format to	*/
	int	x, y;		/* X/Y location		*/
	char	*fmt;		/* printf format string	*/
	va_args	args;

		va_start(args);

		x   = va_arg(args, int);	/* GET...	*/
		y   = va_arg(args, int);	/* .. ALL...	*/
		fmt = va_arg(args, char *);	/* .... ARGS..	*/

		vsprintf(buf, fmt, args);

		va_end(args);
	}

     I have no specific reason to doubt the portability, but it
strikes me that since args are so crazy on various machines (stack
grows up, stack grows down, params passed in registers, etc.)
that letting varargs handle *all* the details would not be a bad
idea.

     Just a thought,
     Steve
-- 
Life : Stephen J. Friedl @ V-Systems, Inc/Santa Ana, CA    *Hi Mom*
CSNet: friedl%vsi.uucp@kent.edu  ARPA: friedl%vsi.uucp@uunet.uu.net
uucp : {kentvax, uunet, attmail, ihnp4!amdcad!uport}!vsi!friedl

gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/28/88)

In article <51@vsi.UUCP> friedl@vsi.UUCP (Stephen J. Friedl) writes:
>     I have no specific reason to doubt the portability, but it
>strikes me that since args are so crazy on various machines (stack
>grows up, stack grows down, params passed in registers, etc.)
>that letting varargs handle *all* the details would not be a bad
>idea.

In fact that's the right way to use varargs.  Declaring part of
the argument list is the wrong way.  (This is the opposite of
ANSI C <stdarg.h> macros, where there MUST be at least one
"anchor" argument declared right before the variadic part.)

dheller@cory.Berkeley.EDU (Dan Heller) (02/28/88)

In article <7361@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <51@vsi.UUCP> friedl@vsi.UUCP (Stephen J. Friedl) writes:
>>     I have no specific reason to doubt the portability, but it
>>strikes me that since args are so crazy on various machines (stack
>>grows up, stack grows down, params passed in registers, etc.)
>>that letting varargs handle *all* the details would not be a bad
>>idea.
>
>In fact that's the right way to use varargs.  Declaring part of
>the argument list is the wrong way.  (This is the opposite of
>ANSI C <stdarg.h> macros, where there MUST be at least one
>"anchor" argument declared right before the variadic part.)

I have reservations about wanting to use varargs for _all_ the arguments
passed to the function.  While lint be be told not to "complain", it avoids
lint's ability to find potential mistakes in the calling routines.  The
example that the original poster used was a good one (altho I don't remember
it exactly, it went something like):

    function(X, Y, fmt, args)
    int X, Y;
    char *fmt;
    va_dcl

Next, to help the lint (and the reader of someone else's code), we can
add:

    /*VARARGS3*/
    function(X, Y, fmt, args)

Stephan has an interesting point:
>>strikes me that since args are so crazy on various machines (stack
>>grows up, stack grows down, params passed in registers, etc.)
>>that letting varargs handle *all* the details would not be a bad
>>idea.

I refute that claiming that it shouldn't be necessary to worry about
it.  A good compiler will pass the arguments correctly and the architecture
should be completely transparent to the programmer.  Declaring the _known_
arguments makes the code more easily linted and more easily understood by
others readers.
			...dan

gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/28/88)

In article <1078@pasteur.Berkeley.Edu> dheller@cory.Berkeley.EDU.UUCP (Dan Heller) writes:
>I refute that claiming that it shouldn't be necessary to worry about it.

But since in fact it is necessary to worry about it, that's not a good
refutation.

>A good compiler will pass the arguments correctly and the architecture
>should be completely transparent to the programmer.

Sorry, in order for the architecture to be "transparent" across all
systems, you have to use varargs as I said.

>Declaring the _known_ arguments makes the code more easily linted and
>more easily understood by others readers.

Which doesn't do much good if it won't run.

blarson@skat.usc.edu (Bob Larson) (02/28/88)

In article <1078@pasteur.Berkeley.Edu> dheller@cory.Berkeley.EDU.UUCP (Dan Heller) writes:
>In article <7361@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>>In article <51@vsi.UUCP> friedl@vsi.UUCP (Stephen J. Friedl) writes:
>>>that letting varargs handle *all* the details would not be a bad
>>>idea.

>>In fact that's the right way to use varargs.  Declaring part of
>>the argument list is the wrong way.  (This is the opposite of
>>ANSI C <stdarg.h> macros, where there MUST be at least one
>>"anchor" argument declared right before the variadic part.)

>I have reservations about wanting to use varargs for _all_ the arguments
>passed to the function.  While lint be be told not to "complain", it avoids
>lint's ability to find potential mistakes in the calling routines.

The versions of lint I have used document, but do not support /*VARARGS0*/
(Sun 4.0 beta reportadly fixes this bug.)  Lint will complain about correct
use of varargs because of this bug.

Writing non-portable code to shut lint up is something I consider very
poor practice.

>I refute that claiming that it shouldn't be necessary to worry about
>it.

Claiming something doesn't make it so.  I claim that compiler writers
shouldn't have to worry about non-portable code.  (Note that my claim
seems to be supported by the ANSI C commitee.)

Guy Harris and I had a long email discussion relating to this, and
there were only two cases where he was able to convince me
/*VARARGSn*/ where n!=0 should be allowed: where portability checking
was explictily turned off, and lint libraries.
--
Bob Larson	Arpa: Blarson@Ecla.Usc.Edu	blarson@skat.usc.edu
Uucp: {sdcrdcf,cit-vax}!oberon!skat!blarson
Prime mailing list:	info-prime-request%fns1@ecla.usc.edu
			oberon!fns1!info-prime-request

ta-dw30@cunixa.cc.columbia.edu (David Worenklein) (05/14/91)

I know I can pull a variable number of arguments off of the stack using va_...
but how do I know how many arguments are there? Is there any way to get the
C compiler to push all the arguments and then push the number of (arguments,
bytes, etc.) pushed?
Thanks.
PS I'm using Symantec Think C v4 so if it has some feature that ANSI doesn't...

===============================================================================
David C Worenklein      | No one could tell me where my soul might be;
Columbia College '93    | I searched for God, but he eluded me;
in the City of New York | I sought my brother out, and found all three. -Crosby

steve@taumet.com (Stephen Clamage) (05/15/91)

ta-dw30@cunixa.cc.columbia.edu (David Worenklein) writes:

>I know I can pull a variable number of arguments off of the stack using va_...
>but how do I know how many arguments are there? Is there any way to get the
>C compiler to push all the arguments and then push the number of (arguments,
>bytes, etc.) pushed?
>PS I'm using Symantec Think C v4 so if it has some feature that ANSI doesn't...

There is no portable way to do this; I don't know whether Think C
provides some extension.  One of the requirements of writing such a
function is that one of the fixed arguments must explicitly or
implicitly provide information about the number and type of the
arguments.  For example, the printf/scanf families interpret the
fixed string argument to determine what the remaining arguments are.

If you have a function which expects a variable number of args all of
the same type, it would be sufficient to have a fixed arg containing
the number of args.  Alternatively, if there is an "impossible" value
for the arg type (NULL pointer, a count of -1), you can use that
value as a flag for the end of the args.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

daw@cbnewsh.att.com (David Wolverton) (05/15/91)

In article <726@taumet.com>, steve@taumet.com (Stephen Clamage) writes:
> ta-dw30@cunixa.cc.columbia.edu (David Worenklein) writes:
> 
> >I know I can pull a variable number of arguments off of the stack using va_...
> There is no portable way to do this; I don't know whether Think C
> provides some extension.
> [good suggestions deleted]

Please think long and hard about using such an extension, even if
it is available.  You'll save a lot of work on the part of the
person who has to port the code next year to another compiler or
environment that doesn't have the neat extension.
And remember, that person may be you.

Stephen gives some good approaches to identifying the length and/or
end of the list -- use them!


Dave Wolverton
David.Wolverton@att.com

dave@aspect.UUCP (Dave Corcoran) (05/20/91)

In article <1991May13.210257.20240@cunixf.cc.columbia.edu>, ta-dw30@cunixa.cc.columbia.edu (David Worenklein) writes:
> I know I can pull a variable number of arguments off of the stack using va_...
> but how do I know how many arguments are there? Is there any way to get the
> C compiler to push all the arguments and then push the number of (arguments,
> Thanks.

Email bounced so...

m4 can help if you want to pass the argument count as the first argument

run this through m4 (/usr/5bin/m4 if BSD):
--------------------8<--------------------
define(`numarg_',`$#,$*')
func(numarg_(asdf,qwer,wqer,s,dfa,fdsa,qwre,sdfa,f,qwer,ewrq,sfd))
func(numarg_(asdf,asd,f))
func(numarg_(qwer,"qwer asfd",&asdf,*vzxc))
func(numarg_("caveat:,commas,in,quotes,are,computed,in,count"))
--------------------8<--------------------
-- 
David Corcoran		      -@@
uunet!aspect!dave	        ~
Having the right to do something is not the same as being right in doing it.
					--  C.K. Chesterson