Article View: comp.lang.c.moderated
Article #2076Variable param for non-vararg subr. calls
From: John Shalf
Date: Mon, 26 Feb 1996 12:50
Date: Mon, 26 Feb 1996 12:50
122 lines
5413 bytes
5413 bytes
<Note to moderator>:
I don't know if this is appropriate for this
conference but I've found comp.lang.c filled with newbie C crap and
comp.std.c filled with religious wars over default type promotion in
the ANSI standard. Hopefully this conf. is the place to ask arcane
questions.
</Note to moderator>
[Although C, of course, just uses stdarg, this is an interesting
question, and I can't think of a better place. -mod]
Varargs allows you to unpack a variable number of arguments from a
subroutine call at runtime. You would think there would be a way to
*pack* a variable number of arguments for a subroutine call at runtime.
However the C faq says;
15.12: 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. Ideally, you should provide a version
of that other function which accepts a va_list pointer
(analogous to vfprintf(); see question 15.5 above). If the
arguments must be passed directly as actual arguments, or if you
do not have the option of rewriting the second function to
accept a va_list (in other words, if the second, called function
must accept a variable number of arguments, not a va_list), no
portable solution is possible. (The problem could perhaps be
solved by resorting to machine-specific assembly language; see
also question 15.13 below.)
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. . . .
As for answer 15.12, I don't have control over the subroutines I am
calling, so I can't use varargs. Both answers 15.12 and 15.13 are
unsatisfactory since clearly many people have already solved
this problem (eg. AVS, Iris Explorer, and perhaps even TK (don't really
have much experience in that case)) I've come up with a couple of
working solutions myself, but I find them to be bletcherous abuses of
C syntax and general programming sanity. My methods also have the
restrictions that all arguments be of the same byte length.
Packing args with variable byte-length would be preferred,
but fixed length gets me pretty far.
Has anyone come up with a saner, more portable method for generating
subroutines calls with a variable number of arguments at runtime?
Here's an example demonstrating a couple of the methods for doing this
which I don't particularly like.
-john
#include <stdio.h>
typedef void(*funcproto)(int,int,int,int);
typedef int farg; /* also played with (void *) for argtypes, but
it ended up being unnecessarily painful */
/* typedefs for for one of the brain damaged conditional methods for
generating calls */
typedef void(*sub0)();
typedef void(*sub1)(int);
typedef void(*sub2)(int,int);
typedef void(*sub3)(int,int,int);
typedef void(*sub4)(int,int,int,int);
void TestSub(int a,int b,int *c)
{
(*c)=a+b;
printf("got a=%u + b=%u ==> c address=%u value=%u\n",a,b,(int)c,*c);
}
void CallConvert(funcproto sub,farg *args,int nargs)
{
printf("nargs=%u\n",nargs);
/* --This method is most compact in representation, but
if I want to allow a reasonable number of parameters
it will exact a pretty large calling overhead
regardless of the number of parameters.
--This works for little-endian machines (or at least so far
little-endian machines appear to push params on the
call stack/regs in this order.
(tested on HP-PA, SGI-MIPS, SUN-SPARC)
sub((nargs>0)?args[0]:0,(nargs>1)?args[1]:0,
(nargs>2)?args[2]:0,(nargs>3)?args[3]:0);
*/
/* --The big-endian version of the above method.
(yeah, so nargs-(<const>-nargs) could be reduced to nargs<<1-<const>
at the expense of a little extra obfuscation... oh well...)
(tested on Macintosh)
sub((nargs>3)?args[nargs-(7-nargs)]:0,(nargs>2)?args[nargs-(6-nargs)]:0,
(nargs>1)?args[nargs-(5-nargs)]:0,(nargs>0)?args[nargs-(4-nargs)]:0);
*/
/* --This version works fine with any architecture, but it
costs a whole lot more per argument when you get to longer
argument lists because most
compilers implement switch in the stupidest possible
manner (instead of a jump table which would be the
most naturally efficient mapping).
*/
switch(nargs)
{
case 0: ((sub0)sub)(); break;
case 1: ((sub1)sub)(args[0]); break;
case 2: ((sub2)sub)(args[0],args[1]); break;
case 3: ((sub3)sub)(args[0],args[1],args[2]); break;
case 4: ((sub4)sub)(args[0],args[1],args[2],args[3]); break;
}
}
int main()
{
farg args[3];
int nargs=3,c;
args[0]=2;
args[1]=3;
args[2]=(int)(&c);
printf("address of c=%u\n",(int)(&c));
printf("c=%u\n", c );
CallConvert((funcproto)TestSub,args,nargs);
printf("c=%u\n",c);
return 1;
}
Technical Details
Message-ID:
<4gsaai$41m@solutions.solon.com>Path:
arc1-ca.pugleaf.net!solon.com!not-for-mailShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail From: John Shalf <jshalf@ncsa.uiuc.edu> Newsgroups: comp.lang.c.moderated Subject: Variable param for non-vararg subr. calls Date: 26 Feb 1996 06:50:26 -0600 Organization: NCSA Sender: clc@solutions.solon.com Approved: clc@solutions.solon.com Message-ID: <4gsaai$41m@solutions.solon.com> NNTP-Posting-Host: solutions.solon.com X-Mailer: Mozilla 1.12(Macintosh; I; 68K) X-URL: news://news.cso.uiuc.edu/comp.lang.c.moderated