🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.c.moderated
Article #2115

Re: Variable param for non-vararg subr. calls

#2115
From: Konrad Schwarz
Date: Thu, 29 Feb 1996 14:38
100 lines
4864 bytes
In article <4gsaai$41m@solutions.solon.com>, John Shalf <jshalf@ncsa.uiuc.edu> writes:
|> 

|> 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;

		[it's impossible]

|> 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?
|> 

|> 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>

                                                            ^^^^^^^^^^^^^^^^
This confused me at first, but then I realized: precedence error!
(Is this a symptom of SCBD? :-)

|> 	 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);

It surprises me that this is supposed to work.
Consider: if 4 == nargs ==> args [1], args [2], args [3], args [4] passed
          if 3 == nargs ==> 0,        args [0], args [1], args [2] passed
          if 2 == nargs ==> 0,        0,       args [-1], args [0] passed
          if 1 == nargs ==> 0,        0,        0,       args [-2] passed
          if 0 == nargs ==> 0,        0,        0,         0       passed

In any case, this has nothing to do with little-endian vs. big-endian.
I know too little of the Macintosh's architectures to speculate on anything else.
In my experience, arguments get pushed onto the stack in the reverse order,
this makes the first argument have a fixed offset from the start of the frame.
This is the reason why variadic functions work in the first place.  If you
have a machine with a stack that grows downwards, you can find the next argument
by increasing the stack address (assuming that arguments are not passed in
registers).  Many Pascal implementations pass arguments the other way around,
so compilers for multilingual environments often have switches to change
the argument passing order.

I know of no general solution to the problem.  However, if all arguments have
the same type, you can place an upper bound on the number of arguments, and you
are not afraid of invoking a bit of undefined behavior (better if you know that
your input arrays is not very high in your address space), you can always code
the following:

return_type wrapper (int numargs, argtype *args)
{
	assert (4 >= numargs);
	return real_function (*args, args [1], args [2], args [3]);
}

A more type-safe way of doing this is:

# define MAX_ARGS 4

struct args {
	argtype args [MAX_ARGS];
};

return_type wrapper (int, struct args *);

return_type wrapper (int numargs, struct args *a)
{
        assert (4 == MAX_ARGS); /* If this fails, adjust the line below */
	return real_function (*a->args, a->args [1], a->args [2], a->args [3]);
}


Konrad Schwarz
Technical Details
Message-ID: <4h4dpj$8nd@solutions.solon.com>
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
References: <4gsaai$41m@solutions.solon.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
From: schwarz@a3.complang.tuwien.ac.at (Konrad Schwarz)
Newsgroups: comp.lang.c.moderated
Subject: Re: Variable param for non-vararg subr. calls
Date: 29 Feb 1996 08:38:43 -0600
Organization: TU Wien
Sender: clc@solutions.solon.com
Approved: clc@solutions.solon.com
Message-ID: <4h4dpj$8nd@solutions.solon.com>
References: <4gsaai$41m@solutions.solon.com>
NNTP-Posting-Host: solutions.solon.com