[comp.lang.c] Variable Parameters

bagchi@dip.eecs.umich.edu (Ranjan Bagchi) (12/31/89)

        I'm working, currently, on a project that involves a function that
could take any number or arguments.  I would like to pass it only
those arguments, i.e. no "flags" at the end which tell the function
to stop.

        I'm using ANSI C, and believe my options are either to pass
the function an array, or to use the elipsis for unspecified.

	The problems with each is that (for the first), given the array
I have no way of knowing the size of the array (do I?) given only
the array.  For the second, I have no idea how to use an elipsis.
 
	Any ideas?
 
	Thanks...rj
	bagchi.sparky.eecs.umich.edu
 
	(If I'm dealing with something trivial, E-mail, please)
ZZ
 

henry@utzoo.uucp (Henry Spencer) (12/31/89)

In article <1169@zip.eecs.umich.edu> bagchi@dip.eecs.umich.edu (Ranjan Bagchi) writes:
>        I'm working, currently, on a project that involves a function that
>could take any number or arguments.  I would like to pass it only
>those arguments, i.e. no "flags" at the end which tell the function
>to stop.

Your function has to have some way of knowing how many arguments there
are, or which is the last argument.  The language does not provide any
built-in way for you to discover this.  The usual approaches are a
terminator argument of some kind, an argument count passed as the
first argument, or an implicit count embodied in an early argument
(e.g., you can tell how many arguments a printf() has by analyzing
the format string).  You have to do one of these things.

>        I'm using ANSI C, and believe my options are either to pass
>the function an array, or to use the elipsis for unspecified.

By far the simplest method is to pass an array with either a terminator
flag in the array or a count as a separate argument.  The ellipsis will
not solve the problem for you, and proper use of it is complex.
-- 
1972: Saturn V #15 flight-ready|     Henry Spencer at U of Toronto Zoology
1989: birds nesting in engines | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

amull@Morgan.COM (Andrew P. Mullhaupt) (12/31/89)

I need to make assignment of any number of variables of any type
to a temporary stack, and then reassign them back again. I'm at
present using two versions (one with varargs.h for System V and
one with stdarg.h for ANSI C). I pass the 'return addresses' and
sizes of the variables, a single punctuational NULL, and then the
list of values as function arguments. I then memcpy the appropriate
sizes for the values (indexed just after the NULL marker argument)
into the waiting addresses. This (for those who may be interested
in speed) is not too slow - on a CISC architecture like the 386 it's
only about 2.5 times as much time as the equivalent structure
assignment, (which can't be coded because the arguments are not always
of some given type), but on a RISC (like a Sun 4) it's about 20 times
as slow. (Gack!). 

Now this business is not without at least one fine point I can't
really get around; and that is the size of a variable is not always
the same as it's size as an argument passed to a function on a 
stack. The complicated part (aside from the instant Hell of memory
model complications) seems to come when using type char data which
gets widened to int. It seems that where your character ends up in the 
int cannot be known without knowledge of the dreaded byte-ordering
of the machine. Also; since there is no way to know at run-time 
what type the argument has, sizeof isn't so helpful on the way into
this function. Essentially, I'm trying to write a 'remember' function
which retains the values of any collection of assignable l-values
for future re-assignment. The function is of type void, but could
be assumed to be called 'recursively' in the sense that evaluation
of the r-values may call functions which call 'remember'. Has
anyone got a standard or portable way to do this? 

P.S. Is there some strange side effect of going from System V style
varargs to ANSI stdarg which changes the code generation between
caller and called? The profile timings of the two otherwise
identical versions of a function using 'remember' compiled on the
SCO / Microsoft UNIX System V/386 r3.2 C compiler differ in how
the time is split between the caller and the called routine. The
ANSI version has a faster called routine, but the System V UNIX-style
version makes up the difference (nearly exactly) by having a faster
caller. Is this due to the dPANS?

Thanks in Advance,
Andrew Mullhaupt

davidsen@sixhub.UUCP (Wm E. Davidsen Jr) (12/31/89)

henry@utzoo.uucp (Henry Spencer) writes:

| Your function has to have some way of knowing how many arguments there
| are, or which is the last argument.  

  I have wondered about this. B certainly had this, when I did my first
B port, about 15-18 years ago, the GCOS version called via a tsx7 and
generated an inline data block in the code which held (a) the number of
arguments, and (b) the address of the calling procedure name, to make
call trace easy.

  When I designed a language based on B, called IMP, it kept that
particular part of the implementation. It was later running on GCOS,
Varian620i, and Intel 8080 under both ISIS and CP/M. The procedure which
returned the number of args was called nargs().

  Where did that come from? I not only don't remember it in BCPL, I
just looked through my manual and don't find it (at least in the
index). If a portable varargs had been designed in at the start of the
language it would have saved a lot of future problems. Even nargs()
would help. I wonder if it was decided to leave it out of C, or if it
by oversight just never got in.

  Better to have C where a few things didn't get in, than PL/1 or Ada,
where everything got in.
-- 
	bill davidsen - sysop *IX BBS and Public Access UNIX
davidsen@sixhub.uucp		...!uunet!crdgw1!sixhub!davidsen

"Getting old is bad, but it beats the hell out of the alternative" -anon

henry@utzoo.uucp (Henry Spencer) (01/01/90)

In article <363@sixhub.UUCP> davidsen@sixhub.UUCP (bill davidsen) writes:
>| Your function has to have some way of knowing how many arguments there
>| are, or which is the last argument.  
>... If a portable varargs had been designed in at the start of the
>language it would have saved a lot of future problems. Even nargs()
>would help. I wonder if it was decided to leave it out of C, or if it
>by oversight just never got in.

Actually nargs() existed in early implementations, although it was always
somewhat defective in that it reported the number of words of arguments
rather than the number of arguments.  (In BCPL the two numbers are always
the same, but not in C.)  It departed due to growing implementation
difficulties.

The fundamental problem with finding out how many arguments you have --
ignoring the problem of varying sizes, which got much worse when C
acquired struct passing -- is that in general you need cooperation
from the caller.  On some machines, the information can be deduced
from details of the stack frame or the calling sequence, but on many
modern systems, it has to be explicitly provided at significant cost.
Given the call-intensive nature of a lot of C programs, it is a seriously
bad idea to incur such cost on every function call, given that most
functions never need the information.

Eventually there was enough demand for somebody at Bell Labs (Dennis?)
to invent <varargs.h>.  (No, like so many other things credited to
Berkeley because they don't put credits in their manual pages, it was
*not* invented at Berkeley.  It existed in V7, although it was not
documented.)  X3J11 ended up fiddling with it a bit to make it more
portable, but the facility finally does exist officially.  However,
you *still* need to have some way of figuring out how many arguments
you've got -- the varargs stuff won't tell you.  Given that it is now
not kosher to use a varargs function without a prototype in scope, I
suppose one could mandate a way to find out the size of the argument
list, but the problem of differing sizes remains.
-- 
1972: Saturn V #15 flight-ready|     Henry Spencer at U of Toronto Zoology
1989: birds nesting in engines | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

davidsen@sixhub.UUCP (Wm E. Davidsen Jr) (01/02/90)

In article <1990Jan1.004914.25006@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:

| The fundamental problem with finding out how many arguments you have --
| ignoring the problem of varying sizes, which got much worse when C
| acquired struct passing -- is that in general you need cooperation
| from the caller.  On some machines, the information can be deduced
| from details of the stack frame or the calling sequence, but on many
| modern systems, it has to be explicitly provided at significant cost.

  As I mentioned, the original versions which with I worked in the 70's
did have the caller provide the information, but the cost was only one
word in the call sequence (no CPU overhead). As common as calls are in
C, they are a very small % of the code. Because of the size problems you
mentioned, it is very difficult to deduce the number of arguments from
the call frame, even when the size of the frame can be determined.

  Varargs is a workable solution to the problem, and I certainly don't
suggest that there is a better simple one. Having to have both address
and type information for the arguments certainly lends itself to
cumbersome solutions.

-- 
	bill davidsen - sysop *IX BBS and Public Access UNIX
davidsen@sixhub.uucp		...!uunet!crdgw1!sixhub!davidsen

"Getting old is bad, but it beats the hell out of the alternative" -anon

henry@utzoo.uucp (Henry Spencer) (01/03/90)

In article <366@sixhub.UUCP> davidsen@sixhub.UUCP (bill davidsen) writes:
>| ... On some machines, the information can be deduced
>| from details of the stack frame or the calling sequence, but on many
>| modern systems, it has to be explicitly provided at significant cost.
>
>  As I mentioned, the original versions which with I worked in the 70's
>did have the caller provide the information, but the cost was only one
>word in the call sequence (no CPU overhead). As common as calls are in
>C, they are a very small % of the code...

They are not a small percentage of the time, however -- many C programs
can spend a *lot* of time calling and returning if the sequences are
inefficient.  (There are C programs, I'm told, which spend 50% of their
time in call/return on a VAX.)  Reserving a word somewhere to hold the
argument count is reasonable... but *where*?  The code may not be readable
at all (in fact, that was the reason why nargs() originally disappeared
from C).  Getting the value into data space, in general, takes run-time
overhead on calls, and there is generally a considerable performance win
in trimming every last unnecessary nanosecond out of calls.

With ANSI C requiring prototypes to be in scope for varargs calls, there
are now compilers that use different calling sequences for varargs and
non-varargs functions.  If everyone could be persuaded to do that, the
efficiency argument would be largely moot:  a bit of overhead in varargs
calls, and *only* varargs calls, is not a problem (indeed, there is often
considerable overhead there anyway).
-- 
1972: Saturn V #15 flight-ready|     Henry Spencer at U of Toronto Zoology
1990: birds nesting in engines | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

econrad@thor.wright.edu (Eric Conrad) (01/03/90)

From article <1169@zip.eecs.umich.edu>,
    by bagchi@dip.eecs.umich.edu (Ranjan Bagchi):
> 
>         I'm working, currently, on a project that involves a function that
> could take any number or arguments.  I would like to pass it only
> those arguments, i.e. no "flags" at the end which tell the function
> to stop.

If I understand the ANSI C mechanism, which uses varargs.h, you have to
pass either some indication of the number of parameters or a sentinel
parameter.  I used this method once several months ago and found it a
bit cumbersome.

An alternative is to write a stack handling module and use it to push
and pop parameters.  The pop function could then return an stack empty
status.  The additional source code would be on the push side.

-- Eric Conrad
+----------------------------------------------------------+
| Eric Conrad - Wright State University                    |
| "Progress was all right once, but it went on too long."  |
+----------------------------------------------------------+