main (int argc, char **argv) {
va_list ap;
...
/* Make ap contain all args from argv[2] to argv[argc-1] */
...
vprintf(argv[1], ap);
}
So, for instance, I could run
<program> "Second arg is %s\n" ABC
Is there a way to do this *correctly* (ANSI-wise) and *portably*?
Kevin Theobald
McGill University
: You are confusing two things here. C allows you to write functions
: which can be called with a varying number (and types) of arguments,
: but you can't construct a call which has a varying number of
: arguments. Examples of calling a var-arg function btw. are any time
: you call one of the printf() or scanf() functions.
That's not the point. I want to call vprintf (see above), which uses
a special structure for holding a variable-argument list, as opposed to
printf, which is called in the standard way. There are library routines
that are used for reading arguments in this special structure one at a
time; this ensures portability. If this structure can be "deconstructed"
(ooooh, I'm so PC! :-) by the user, can it also be constructed by the user?
: switch(argc)
: {
: case 2: printf("%s", argv[1]);
: case 3: printf("%s %s", argv[1], argv[2]);
...
In fact, to do what the code above does, it would have to be:
switch(argc)
{
case 2: printf(argv[1], argv[2]);
case 3: printf(argv[1], argv[2], argv[3]);
...
but either way: yeech!
Kevin
Here's what the FAQ list has to say:
15.13: How can I call a function with an argument list built up at run
time?
A: There is no guaranteed or portable way to do this. If you're
curious, ask this list's editor, who has a few wacky ideas you
could try...
Somehow I've never gotten around to writing the definitive essay
on those "wacky ideas" (or if I have, the message is lost in my
archives). Here are three messages I've written on the subject
at various times; they summarize most of the ideas, and should at
least get you started. (There is some overlap, because each of
the later ones was written when I didn't have copies of the
earlier ones handy.)
Steve Summit
s...@eskimo.com
* * *
From: s...@adam.pika.mit.edu (Steve Summit)
Newsgroups: comp.unix.wizards,comp.lang.c
Subject: Re: Needed: A (Portable) way of setting up the arg stack
Keywords: 1/varargs, callg
Message-ID: <11...@bloom-beacon.MIT.EDU>
Date: 4 Jun 89 16:43:52 GMT
Lines: 168
In article <7...@mitisft.Convergent.COM> kem...@mitisft.Convergent.COM (Gregory Kemnitz) writes:
>I need to know how (or if) *NIX (System V.3) has the ability to let
>a stack of arguments be set for a function before it is called. I
>have several hundred pointers to functions which are called from one
>place, and each function has different numbers of arguments.
A nice problem. Doug Gwyn's suggestion is the right one, for
maximum portability, but constrains the form of the called
subroutines and also any calls that do not go through the
"inverse varargs mechanism." (That is, you can't really call the
subroutines in question without building the little argument
vector.)
For transparency (at some expense in portability) I use a routine
I call "callg," named after the VAX instruction of the same name.
(This is equivalent to Peter Desnoyers' "va_call" routine; in
retrospect, I like his name better.)
va_call can be implemented in one line of assembly language on
the VAX; it typically requires ten or twenty lines on other
machines, to copy the arguments from the vector to the real stack
(or wherever arguments are really passed). I have implementations
for the PDP11, NS32000, 68000, and 80x86. (This is a machine
specific problem, not an operating system specific problem.) A
routine such as va_call MUST be written in assembly language; it
is one of the handful of functions I know of that cannot possibly
be written in C.
Not all machines use a stack; some use register passing or other
conventions. For maximum portability, then, the interface to a
routine like va_call should allow the type of each argument to be
explicitly specified, as well as hiding the details of the
argument vector construction. I have been contemplating an
interface similar to that illustrated by the following example:
#include "varargs2.h"
extern printf();
main()
{
va_stack(stack, 10); /* declare vector which holds up to 10 args */
va_push(stack, "%d %f %s\n", char *);
va_push(stack, 12, int);
va_push(stack, 3.14, double);
va_push(stack, "Hello, world!", char *);
va_call(printf, stack);
}
Note that this calls the standard printf; printf need take no
special precautions, and indeed cannot necessarily tell that it
has not been called normally. (This is what I meant by
"transparency.")
On a "conventional," stack-based machine, va_stack would declare
an array of 10 ints (assuming that int is the machine's natural
word size) and va_push would copy words to it using pointer
manipulations analogous to those used by the va_arg macro in the
current varargs and stdarg implementations. (Note that "declare
vector which holds up to 10 args" is therefore misleading; the vector
holds up to 10 words, and it is up to the programmer to leave
enough slop for multi-word types such as long and double. The
distinction between a "word" and an "argument" is the one that
always comes up when someone suggests supplying a va_nargs()
macro; let's not start that discussion again.)
For a register-passing machine, the choice of registers may
depend on the types of the arguments. For this reason, the
interface must allow the type information to be retained in the
argument vector for inspection by the va_call routine. This
would be easier to... implement if manifest constants were used,
instead of C type names:
va_push(stack, 12, VA_INT);
va_push(stack, 3.14, VA_DOUBLE);
va_push(stack, "Hello, world!", VA_POINTER);
Since it would be tricky to "switch" on these constants inside
the va_push macro to decide how many words of the vector to set
aside, separate push macros might be preferable:
va_push_int(stack, 12);
va_push_double(stack, 3.14);
va_push_pointer(stack, "Hello, world!");
(This option has the additional advantage over the single va_push
in that it does not require that the second macro argument be of
variable type.) There is still a major difficulty here, however,
in that one cannot assume the existence of a single kind of
pointer.
For the "worst" machines, the full generality of C type names (as
in the first example) would probably be required. Unfortunately,
to do everything with type names you might want to do, you have
to handle them specially in the compiler. (On the other hand,
the machines that would have trouble with va_push are probably
the same ones that already have to have the varargs or stdarg
mechanisms recognized by the compiler.)
Lest you think that va_call, if implementable, solves the whole
problem, don't let your breath out yet: what should the return
value be? In the most general case, the routines being indirectly
called might return different types. The return value of va_call
would not, so to speak, be representable in closed form.
This last wrinkle (variable return type on top of variable
arguments passed) is at the heart of a C interpreter that allows
intermixing of interpreted and compiled code. I know how I
solved it; I'd be curious to know how Saber C solves it. (I
solved it with two more assembly language routines, also
unimplementable in C. A better solution, to half of the problem,
anyway, would be to to provide a third argument, a union pointer
of some kind, to va_call for storing the return value.)
I just whipped together an implementation of the first example,
which I have appended for your edification and amusement, as long
as you have a VAX. [See "Listing 1" at the end of this
collection of messages.]
Steve Summit
s...@adam.pika.mit.edu
* * *
Newsgroups: comp.lang.c
From: s...@adam.mit.edu (Steve Summit)
Subject: Re: varargs fn calling varargs fn?
Message-ID: <1992Jul14.2...@athena.mit.edu>
Date: Tue, 14 Jul 1992 21:46:25 GMT
Lines: 262
In article <1992Jul9.1...@shearson.com>, Frank Greco (fgr...@shearson.com) writes:
> Does anyone have any examples of a C function that uses varargs
> that calls another C function that uses the same args?
> ...
> The man page for varargs(3) sez:
>
> The argument list (or its remainder) can be passed to
> another function using a pointer to a variable of type
> va_list()...
>
> What do they mean "pointer to a variable of type va_list()? I've tried
> several combinations and bus-erroring away...
And in article <1992Jul10.0...@infodev.cam.ac.uk>, Colin Bell (cr...@cus.cam.ac.uk) writes:
> I've solved this one, but have a related one of my own. I have a function which handles
> message handling for a system: ie takes a list of arguments as one would send to
> fprintf prefixed by two or three telling the function what to do with the resulting
> string. What I would like to do is strip these arguments off the front and then pass
> the entire remainder to sprintf, and deal with the resulting string.
[Side note: try to keep the line length in posted articles to
well under 80 characters; the above looks pretty ugly to those of
us with 80 character screens/windows.]
Preliminary answers to both of these questions can be found in
the comp.lang.c frequently-asked questions (FAQ) list:
6.2: How can I write a function that takes a format string and a
variable number of arguments, like printf, and passes them to
printf to do most of the work?
A: Use vprintf, vfprintf, or vsprintf.
Here is an "error" routine which prints an error message,
preceded by the string "error: " and terminated with a newline:
[sample code deleted]
References: K&R II Sec. 8.3 p. 174, Sec. B1.2 p. 245; H&S
Sec. 17.12 p. 337; ANSI Secs. 4.9.6.7, 4.9.6.8, 4.9.6.9 .
6.4: How can I write a function which takes a variable number of
arguments and passes them to some other function (which takes a
variable number of arguments)?
A: In general, you cannot. You must provide a version of that
other function which accepts a va_list pointer, as does vfprintf
in the example above. If the arguments must be passed directly
as actual arguments (not indirectly through a va_list pointer)
to another function which is itself variadic (for which you do
not have the option of creating an alternate, va_list-accepting
version) no portable solution is possible. (The problem can be
solved by resorting to machine-specific assembly language.)
Now, more could of course be said about these issues. In the
case of accepting a format string and some variable arguments and
some other arguments, passing the format string and the variable
arguments to vsprintf, and then doing something with the string
produced by vsprintf and the other arguments, there is a real
problem in that an appropriate or guaranteed-adequate size for
vsprintf's return buffer cannot readily be determined. (How to
attempt to do so is itself a frequently-asked question which the
FAQ list should probably address. I'll say no more about it
today, except to lament the fact that there is unfortunately no
good, portable solution.)
Frank Greco asked, "What do they mean `pointer to a variable of
type va_list()'?". There's apparently a typo in his manual, but
the issue of variable-length argument lists versus "pointers" to
same is a potentially confusing one. Here is how I think about
them, extracted from an e-mail discussion I had last Spring with
someone about question 6.4 in the FAQ list:
* * *
...one often speaks of "the variable-length part of a
variable-length argument list," which is kind of a silly
mouthful, but it is often important to distinguish the
variable-length part from the fixed part, which in ANSI C must
always exist. (For example, printf always takes one char *
argument -- the "fixed" part -- followed by 0 or more arguments
of arbitrary types.)
* * *
[Passing a va_list pointer along to another routine] is the
preferred way of doing things, but note that a function that
accepts a single va_list pointer is not at all variadic.
(As far as C is concerned, it accepts exactly one argument.)
This is the solution alluded to by the (admittedly rather opaque)
FAQ list wording
You must provide a version of that other function which
accepts a va_list pointer, as does vfprintf in the
example above.
That is, the answer to the question "Can I write a debug_printf()
in terms of printf()?" is "No." printf() is variadic, but
vprintf et al take va_list pointers, so you *can* write a
debug_printf() in terms of one of them.
* * *
"Variadic" means (at least as I use it) very specifically that a
subroutine, as the C compiler sees it, accepts a variable number
of formal parameters. (Sometimes the term "formal parameter" is
used when we must distinguish very carefully between the generic
things a subroutine is set up to receive and the "actual
arguments" which are passed to it by a specific subroutine call.)
It is fairly easy to prove that one cannot call a variadic
function with an argument list which varies at run time (which is
what you need to do if you're going to use the variable-length
argument list that another variadic function has just received).
The only way that a C compiler will generate a subroutine call is
when you write an expression of the form
<function> ( <argument-list> )
where <function> is an expression which evaluates to a function
(either the name of a function, or the "contents" of a pointer-
to-function), and <argument-list> is a list of 0 or more comma-
separated expressions. Obviously, since they appear in the
source code, there must be a fixed number of arguments (in any
one call).
* * *
[When a variadic function tries to pass the variable arguments
along to another, it] doesn't matter whether or not the first
function does any additional "processing" or not, and it doesn't
matter whether the second function uses stdarg.h or not (there
are other, less portable methods of accessing variable-length
argument lists, and the second function might not even be written
in C). The essential question is whether a variadic function can
call another variadic function (with the strict interpretation of
"variadic" which I use), passing to the second function the
(indeterminate number of) arguments which it receives (at
run-time) in a particular call. [Once again, the answer is that
it cannot.]
If and when I fix up this question, I'm going to introduce the
following explanation, which should help to clarify things in
your mind if they're not clear already.
Just as we can use both integers and pointers-to-integers
in C, there are also two ways of manipulating variable-
length argument lists. A function such as printf accepts
a variable-length argument list -- we cannot see exactly
how many arguments printf will be called with. However,
we can also conceive of a "pointer to a variable-length
argument list," and this is exactly what a va_list is.
When one is dealing with pointers, one has to be very
careful to distinguish between the pointer and the thing
pointed to. It is invariably incorrect to attempt to use
a pointer value when one intends to use the value
pointed to. Explicit syntax, e.g. the unary * operator,
is used to dereference a pointer, i.e. to access the
value pointed to.
It is similarly meaningless and/or impossible to use a
pointer to a variable-length argument list in place of an
actual variable-length argument list, or vice versa.
[For example, you cannot pass a va_list to printf, nor
can you call vprintf with a variable number of arguments.]
Now, it happens that in C we as programmers do not have
as much control over, and freedom with respect to,
variable-length argument lists (or fixed-length argument
lists, for that matter). In particular, we cannot create
a variable-length argument list, nor can we pick an
argument out of a variable-length argument list.
(We can do only a little bit more with fixed-length
argument lists. We can "create" a fixed-length argument
list only by writing a subroutine-call expression in a
piece of source code, and we can pick an argument out of
a fixed-length argument list only by naming it, in the
context of a subroutine definition, and referring to it
by name.)
The only thing we, as programmers, can do with
variable-length argument lists are:
1. Given a variable-length argument list, generate a
pointer to it, using the va_start macro. (Under
this analysis, va_start is analogous to the unary
& operator.)
2. Given a pointer to a variable-length argument
list, (i.e. a va_list), pick an argument (of a
particular type) out of it.
However, we *can* manipulate a va_list (a pointer to a
variable-length argument list) with almost as much
freedom as we manipulate other objects in C. In
particular, we can pass a va_list to another subroutine.
There is, however, neither a syntax by which we could
declare an "object" of "type" "variable-length argument
list," nor a mechanism by which we could initialize one
with a particular value (i.e. with a particular variable-
length argument list). Therefore, we cannot accept a
variable-length argument list in one variadic function
and pass it (as a variable-length argument list) to
another variadic function. What we can do is to take a
pointer (a va_list) to the variable-length argument list
(using va_start), and pass that pointer (as a single
argument) to another function; however, that other,
va_list-accepting function is typically not variadic.
Examples of variadic functions, which accept variable-
length argument lists, are printf, fprintf, and sprintf.
Examples of non-variadic functions, which accept pointers
to variable-length argument lists as single va_lists, are
vprintf, vfprintf, and vsprintf.
As a point of general advice, whenever one writes a
function which accepts a variable number of arguments, it
is a good idea to write a companion routine which accepts
a single va_list. Writing these two routines is no more
work than writing just the one, because the first,
variadic function can be implemented very simply in terms
of the second -- it just has to call va_start, then pass
the resultant va_list to the second function.
By implementing a pair of functions, you leave the door
open for someone (you or anyone else) later to build
additional functionality "on top" of the function(s), by
writing another variadic function (or, better, a variadic
and non-variadic pair) which calls the second, va_list-
accepting, lower-level function as well as doing some
additional processing.
If you were to write only a single, variadic function,
later programmers who tried to implement additional
functionality on top of it would find themselves in the
same situation as did people trying to write things like
debug_printf() in the days before the v*printf family
had been invented: they had no portable way to "hook up"
to the core functionality provided by the *printf family;
they had to use nonportable kludges to call printf with
(at least some of) the (variable) arguments which the
higher-level functions received, or they had to abandon
printf entirely and reimplement some or all of its
functionality.
Obviously, that's far too long-winded and repetetetive for the
FAQ list, but it's a good first draft for me to try to whittle
down and distill the essence out of. (There's also a slight
imperfection in its conceptual model -- although it alludes to
"objects of type `variable-length argument list,'" there are
really no values of type "variable-length argument list." All
actual argument lists obviously have a certain number of
arguments, so an "object of type `variable-length argument list'"
is just one that can hold arbitrary fixed-length argument lists,
regardless of how long they are.)
Steve Summit
s...@adam.mit.edu
* * *
Date: Sun, 7 Mar 93 17:58:52 -0500
Message-Id: <930307225...@adam.MIT.EDU>
From: s...@adam.mit.edu (Steve Summit)
Subject: Re: dynamic function call
[Someone asked me in e-mail for the "wacky ideas"; I did not have
the preceding two messages available at the time.]
Believe it or not, you're the first to have asked, so you get to
be the first to hear me apologize for the fact that several
elaborate discussions of those "wacky ideas" which I've written
at various times are in fact stuck off in my magtape archives
somewhere, not readily accessible.
Here is an outline; I'll try to find one of my longer old
descriptions and send it later.
The basic idea is to postulate the existence of the following
routine:
#include <stdarg.h>
callg(funcp, argp)
int (*funcp)();
magic_arglist_pointer argp;
This routine calls the function pointed to by funcp, passing to
it the argument list pointed to by argp. It returns whatever the
pointed-to function returns.
The second question is of course how to construct the argument
list pointed to by argp. I've had a number of ideas over the
years on how to do this; perhaps the best (or at least the one
I'm currently happiest with) is to do something like this:
extern func();
va_alloc(arglist, 10);
va_list argp;
va_start2(arglist, argp);
va_arg(argp, int) = 1;
va_arg(argp, double) = 2.3;
va_arg(argp, char *) = "four";
va_finish2(arglist, argp);
callg(func, arglist);
The above is equivalent to the simple call
func(1, 2.3, "four");
Now, the interesting thing is that it's often (perhaps even
"usually") possible to construct va_alloc, va_start2, and
va_finish2 macros such as I've illustrated above such that the
standard va_arg macro out of <stdarg.h> or <varargs.h> does the
real work. (In other words, the traditional implementations of
va_arg would in fact work in an lvalue context, i.e. on the left
hand side of an assignment.)
I'm fairly sure I've got working versions of macros along the
lines of va_alloc, va_start2, and va_finish2 macros somewhere,
but I can't find them at the moment, either. At the end of this
message ["Listing 2"] I'll append a different set of macros, not
predicated on the assumption of being able to re-use the existing
va_arg on the left hand side, which should serve as an example of
the essential implementation ideas.
A third question is what to do if the return type (not just the
argument list) of the called function is not known at compile
time. (If you're still with me, we're moving in the direction of
doing so much at run time that we've practically stopped
compiling and started interpreting, and in fact many of the ideas
I'm discussing in this note come out of my attempts to write a
full-blown C interpreter.) We can answer the third question by
adding a third argument to our hypothetical callg() routine,
involving a tag describing the type of result we expect and a
union in which any return value can be stored.
A fourth question is how to write this hypothetical callg()
function. It is one of my favorite examples of a function that
essentially must be written in assembler, not for efficiency, but
because it's something you simply can't do in C. It's actually
not terribly hard to write; I've got implementations of it for
most of the machines I use. I write it for a new environment by
compiling a simple little program fragment such as
int nargs;
int args[10];
int (*funcp)();
int i;
switch(nargs)
{
case 0: (*funcp)();
break;
case 1: (*funcp)(args[0]);
break;
case 2: (*funcp)(args[0], args[1]);
break;
...
for(i = 0; i < nargs; i++)
(*funcp)(args[i]);
, and massaging the assembly language output to push a variable
number of arguments before performing the (single) call. It's
possible to do this without knowing very much else about the
assembly/machine language in use. (It helps a lot to have a
compiler output or listing option which lets you see the its
generated assembly language.)
When I say it's generally easy to write callg, I'm thinking of
conventional, stack-based machines. Some modern machines pass
some or all arguments in registers, and which registers are used
can depend on the type of the arguments, which makes this sort of
thing much harder.
A fifth question is where my name "callg" comes from. The VAX has a
single instruction, CALLG, which does exactly what you want here.
(In other words, an assembly-language implementation of this
callg() routine is a single line of assembler on the VAX.)
I've also used the name va_call instead of callg.
If you have questions or comments prompted by any of this, or if
you'd like to see more code fragments, feel free to ask.
Steve Summit
s...@adam.mit.edu
* * *
[This is "Listing 1"]
#!/bin/sh
sed 's/^X//' > pf.c <<\EOF
X#include "varargs2.h"
X
Xextern printf();
X
Xmain()
X{
Xva_stack(stack, 10); /* declare vector which holds up to 10 args */
X
Xva_stack_init(stack);
X
Xva_push(stack, "%d %f %s\n", char *);
Xva_push(stack, 12, int);
Xva_push(stack, 3.14, double);
Xva_push(stack, "Hello, world!", char *);
X
Xva_call(printf, stack);
X}
EOF
sed 's/^X//' > varargs2.h <<\EOF
X#ifndef VARARGS2_H
X#define VARARGS2_H
X
X#define va_stack(name, max) int name[max+1]
X#define va_stack_init(stack) stack[0] = 0
X#define va_push(stack, arg, type) *(type *)(&stack[stack[0]+1]) = (arg), \
X stack[0] += (sizeof(type) + sizeof(int) - 1) / sizeof(int)
X
X#ifdef __STDC__
Xextern int va_call(int (*)(), int *);
X#endif
X
X#endif
EOF
sed 's/^X//' > va_call.s <<\EOF
X.text
X
X.globl _va_call
X
X_va_call:
X .word 0
X callg *8(ap), *4(ap)
X ret
EOF
* * *
[This is "Listing 2"]
#ifndef VARARGS2_H
#define VARARGS2_H
#define va_stack(name, max) int name[max+1]
#define va_stack_init(stack) stack[0] = 0
#define va_push(stack, arg, type) *(type *)(&stack[stack[0]+1]) = (arg), \
stack[0] += (sizeof(type) + sizeof(int) - 1) / sizeof(int)
#ifdef __STDC__
extern int va_call(int (*)(), int *);
#endif
#endif
* * *