[comp.lang.c] varargs -> varargs mystery

rick@sparky.IMD.Sterling.COM (Richard Ohnemus) (05/03/91)

In article <3099@cirrusl.UUCP> Rahul Dhesi <dhesi@cirrus.COM> writes:
>Suppose A is a varargs function, and it uses the first argument
>supplied, and passes on the rest to B, which is also a varargs
>function.  What is the right way to do it?  The Sun varargs manual
>entry is a little confusing to me.  It says:
>
>    "The argument list (or its remainder) can be passed to another
>     function using a pointer to a variable of type va_list--in which
>     case a call to va_arg in the subroutine advances the argument-list
>     pointer with respect to the caller as well."
>
>So should I be declaring a variable in A and passing its address to B?
>A does not know many arguments it is receiving and passing on to B.
>All that A knows is the type of the one argument that it itself uses.
>
>Sample code follows.
>
>#include <varargs.h>
>
>int A(va_alist)
>va_dcl
>{
>   int i;
>   va_list ap;
>
>   va_start(ap);
>   i = va_arg(ap, int);
>   ... use i locally, pass remaining args to B ...
>   B(&ap);         <<<<==== Is this what the Sun man page wants?
>   va_end(arg);
>}
>
>int B(va_alist)
>va_dcl
>{
>   ... B uses its variable argument list which it receives from A ...
>}
>--
>Rahul Dhesi <dhesi@cirrus.COM>
>UUCP:  oliveb!cirrusl!dhesi

Just pass the variable argument list pointer instead of the address of 
the pointer.

The following sample code works (Saber-C doesn't complain about and I 
haven't had any core dumps yet.).

#include <varargs.h>

int A(va_alist)
va_dcl
{
   int i;
   va_list ap;

   va_start(ap);
   i = va_arg(ap, int);
   ... use i locally, pass remaining args to B ...
   B(ap);
   va_end(arg);
}

int B(args)
va_list args;
{
   char *cp;

   cp = va_arg(args, char *);
   ... etc.
}

-- 
I never receive credit for anything I write! (I'm an Ohnemus. 8-)

Rick Ohnemus                 UUCP:     uunet!sparky!rick
Sterling Software IMD        INTERNET: rick@sparky.IMD.Sterling.COM
1404 Ft. Crook Rd. South     Phone:    (402) 291-8300 
Bellevue, NE. 68005-2969     FAX:      (402) 291-4362

richard@aiai.ed.ac.uk (Richard Tobin) (05/03/91)

In article <1991May2.170148.12655@sparky.IMD.Sterling.COM> rick@sparky.IMD.Sterling.COM (Richard Ohnemus) writes:
>In article <3099@cirrusl.UUCP> Rahul Dhesi <dhesi@cirrus.COM> writes:
>>Suppose A is a varargs function, and it uses the first argument
>>supplied, and passes on the rest to B, which is also a varargs
>>function.  What is the right way to do it?

It can't be done.  What Rahul wants is the equivalent of Lisp's
"apply", and C doesn't have one.

>Just pass the variable argument list pointer instead of the address of 
>the pointer.

Note that he wanted to pass the arguments on to another varargs
function, not one that took a single va_list argument.

Imagine a function debug() which takes a "debugging level" and and
some arguments that will be passed on to fprintf() if the level is
high enough.  For this particular case C provides the functions
vfprintf() etc.  If there were a (portable) way to do what Rahul
wanted, those functions would be unnecessary.

-- Richard

Richard Tobin,                       JANET: R.Tobin@uk.ac.ed             
AI Applications Institute,           ARPA:  R.Tobin%uk.ac.ed@nsfnet-relay.ac.uk
Edinburgh University.                UUCP:  ...!ukc!ed.ac.uk!R.Tobin

dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) (05/04/91)

After I posted my inquiry I realized that I was asking a
frequently-asked-question, and the FAQ postings already answered it.  I
cancelled my posting, but not soon enough.

Briefly, the question was:  if A and B are varags functions, how can
one portably write them so that A uses one argument and passes the rest
to B?  The short answer is:  you can't.  The long answer is:  you
cannot.

In <4606@skye.ed.ac.uk> richard@aiai.ed.ac.uk (Richard Tobin) writes:

     If there were a (portable) way to do what Rahul wanted, those
     functions [such as vfprintf] would be unnecessary.

Right.  The problem is that A wants to use one argument and pass on the
rest to B, but A doesn't know how many arguments there are.  So it
doesn't know how many to pass on to B.

Nonportably, A could fudge with the stack -- if the arguments were
guaranteed to be there -- and pass on most of the stack intact to B.
In assembly language you can probably do this quite easily.  Even in C,
it should be possible to design compilers to allow this and to have
some way of telling the compiler that it should do this.

But nobody did, so there is no portable way of doing it in C.
--
Rahul Dhesi <dhesi@cirrus.COM>
UUCP:  oliveb!cirrusl!dhesi

catfood@NCoast.ORG (Mark W. Schumann) (05/06/91)

richard@aiai.uucp (Richard Tobin) writes:
>In article <1991May2.170148.12655@sparky.IMD.Sterling.COM> rick@sparky.IMD.Sterling.COM (Richard Ohnemus) writes:
>>In article <3099@cirrusl.UUCP> Rahul Dhesi <dhesi@cirrus.COM> writes:
>>>Suppose A is a varargs function, and it uses the first argument
>>>supplied, and passes on the rest to B, which is also a varargs
>>>function.  What is the right way to do it?
>
>It can't be done.  What Rahul wants is the equivalent of Lisp's
>"apply", and C doesn't have one.
>
>>Just pass the variable argument list pointer instead of the address of 
>>the pointer.
>
>Note that he wanted to pass the arguments on to another varargs
>function, not one that took a single va_list argument.
>
Actually, I think this can be done, but not in any guaranteed
portable way.  (To many, that means it can't be done.  :-)  )

-- 
============================================================
Mark W. Schumann  3111 Mapledale Avenue, Cleveland 44109 USA
Domain: catfood@ncoast.org
UUCP:   ...!mailrus!usenet.ins.cwru.edu!ncoast!catfood

gwyn@smoke.brl.mil (Doug Gwyn) (05/07/91)

In article <3110@cirrusl.UUCP> Rahul Dhesi <dhesi@cirrus.COM> writes:
>Briefly, the question was:  if A and B are varags functions, how can
>one portably write them so that A uses one argument and passes the rest
>to B?  The short answer is:  you can't.

Sure you can, but you have to pass the remaining arguments via a va_list
parameter, not scattered about.  I do this all the time.

ray@philmtl.philips.ca (Ray Dunn) (05/17/91)

In referenced article, gwyn@smoke.brl.mil (Doug Gwyn) writes:
 >In article <3110@cirrusl.UUCP> Rahul Dhesi <dhesi@cirrus.COM> writes:
 >>Briefly, the question was:  if A and B are varags functions, how can
 >>one portably write them so that A uses one argument and passes the rest
 >>to B?  The short answer is:  you can't.
 >
 >Sure you can, but you have to pass the remaining arguments via a va_list
 >parameter, not scattered about.  I do this all the time.

What I've found this means in practice is that when you write a varargs
function it's usually best to write it as *two* functions, fn and vfn,
where fn creates the va_list and passes it vfn where the work is actually
done.

Both fn and vfn can then be called as required.

void
fn(int arg, ...)
{   va_list argsp;
      
    va_start(argsp, arg);
    vfn(arg, argsp);
    va_end(argsp);
}

void
vfn(int arg, va_list argsp)
{
    /* body of code */
}

Note that the example in the FAQ list defines a 'v' named function,
vstrcat, as taking a variable number of arguments not a va_list.

It's better style to keep to the implied convention that 'v' named
functions take a va_list.
-- 
Ray Dunn.                    | UUCP: ray@philmtl.philips.ca
Philips Electronics Ltd.     |       ..!{uunet|philapd|philabs}!philmtl!ray
600 Dr Frederik Philips Blvd | TEL : (514) 744-8987  (Phonemail)
St Laurent. Quebec.  H4M 2S9 | FAX : (514) 744-9550  TLX: 05-824090

gwyn@smoke.brl.mil (Doug Gwyn) (05/17/91)

In article <1991May16.173145.2440@philmtl.philips.ca> ray@philmtl.philips.ca (Ray Dunn) writes:
-What I've found this means in practice is that when you write a varargs
-function it's usually best to write it as *two* functions, fn and vfn,
-where fn creates the va_list and passes it vfn where the work is actually
-done.
-Both fn and vfn can then be called as required.

Exactly right.