kemnitz@mitisft.Convergent.COM (Gregory Kemnitz) (06/02/89)
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. (I suppose
I could use the maximum number of arguments that any function has, but is
this portable??) I know how many arguments there are when I call the function,
and what those arguments are. Is there any alternative (like varargs) to
a big switch block or calling with the max number of arguments??
--
----------------------------------+--------------------------------------
Greg Kemnitz | "He who does not understand baseball
kemnitz@Convergent.COM | will never understand America"
| --Tocqueville
gwyn@smoke.BRL.MIL (Doug Gwyn) (06/02/89)
In article <708@mitisft.Convergent.COM> kemnitz@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. Let's assume for the sake of simplicity that all the arguments have the same type. Then your best bet is to define all the functions to take a pointer to (an array of) arguments. That way they're all conformable.
desnoyer@Apple.COM (Peter Desnoyers) (06/03/89)
In article <708@mitisft.Convergent.COM> kemnitz@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. (I suppose >I could use the maximum number of arguments that any function has, but is >this portable??) I know how many arguments there are when I call the function, >and what those arguments are. Is there any alternative (like varargs) to >a big switch block or calling with the max number of arguments?? >-- >----------------------------------+-------------------------------------- >Greg Kemnitz | "He who does not understand baseball >kemnitz@Convergent.COM | will never understand America" > | --Tocqueville I have run into this problem in writing event-driven simulations, and have solved it two times in two different ways - (a) - use the maximum number of arguments. This won't break on any compiler I know of. However, if you want to be strictly conforming, if f is a function which you are using a pointer to, you should change f( a, b, c) f( a, b, c, ...) /* may be slightly incorrect */ so that it will be guaranteed to be able to digest the surplus arguments. (b) write a non-portable function va_call( f, n, args[]) that calls f with the N arguments in 'args'. You could write it in assembler or use inline assembler to access the stack and frame pointers. Peter Desnoyers
scs@adam.pika.mit.edu (Steve Summit) (06/04/89)
In article <708@mitisft.Convergent.COM> kemnitz@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 be 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. Steve Summit scs@adam.pika.mit.edu #!/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
carroll@s.cs.uiuc.edu (06/05/89)
/* Written 11:58 am Jun 2, 1989 by gwyn@smoke.BRL.MIL in s.cs.uiuc.edu:comp.lang.c */ In article <708@mitisft.Convergent.COM> kemnitz@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. (...) Let's assume for the sake of simplicity that all the arguments have the same type. (...) /* End of text from s.cs.uiuc.edu:comp.lang.c */ If that's not the case, you can wrap the arguments in a union, and pass an array of those, e.g. (assuming you can count on argument types) union Argument { int i; float j; char *s; } ; union Argument arg_list[MAX_ARGS]; /* array to pass */ Alan M. Carroll "And there you are carroll@s.cs.uiuc.edu Saying 'We have the Moon, so now the Stars...'" CS Grad / U of Ill @ Urbana ...{ucbvax,pur-ee,convex}!s.cs.uiuc.edu!carroll
blarson@skat.usc.edu (Bob Larson) (06/05/89)
In article <11830@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: >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.) ??? Then how come my os9/68k compiler for the 68020 uses a different calling convention than the sun 3 compiler? This problem is most definitly dependant on the calling convention your compiler happens to be using at the moment, which can vary with cpu, operating system, and even compiler options. > 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. This almost sounds like a chalange to find a machine where it can be done in non-portable C. I'm sure there is one out there somewhere. -- Bob Larson Arpa: blarson@skat.usc.edu Uucp: {uunet,cit-vax}!usc!skat!blarson Prime mailing list: info-prime-request%ais1@ecla.usc.edu usc!ais1!info-prime-request
desnoyer@Apple.COM (Peter Desnoyers) (06/06/89)
In article <11830@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: > [routine called va_call that takes a function pointer, an argument > count, and an array of arguments] > >va_call can be implemented in one line of assembly language... >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. Almost correct. In Turbo C there are pseudo-variables for the stack pointer, etc. You can also access these registers (warning - approaching gross kludge) by doing a setjmp and knowing the jmp_buf format. Peter Desnoyers
gis@datlog.co.uk ( Ian Stewartson ) (06/10/89)
In article <11830@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes: >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. It seemed to me that the printf and scanf (and in particular the vprintf family) have a similar problem looked at in a mirror. They effectively do what you want in reverse. I noticed that all the printf family use the function _doprnt (possibly not on all systems, but certainly the few that I have access to). Therefore va_call could be implemented in the similar manner. Taking Steve's example code, I hacked it to produce a macro va_access to get the top of the argument stack excluding the count and then modified pf.c to produce the following code: #include <stdio.h> #include "varargs2.h" #define va_access(stack) &stack[1] main() { va_stack(stack, 10); va_stack_init(stack); va_push(stack, 12, int); va_push(stack, 3.14, double); va_push(stack, "Hello, world!", char *); _doprnt ("%d %f %s\n", va_access(stack), stdout); printf ("%d %f %s\n", 12, 3.14, "Hello, world!"); } Which generates the strings: 12 3.140000 Hello, world! 12 3.140000 Hello, world! (The last printf to check the results). The declaration of _doprnt is of the form int _doprnt (format, args, iop) char *format; va_list args; FILE *iop; and the arguments are accessed within _doprnt using width = va_arg(args, int); There being no va_start and va_end in the function. Now I realised that _doprnt is a special case on Unix systems and may not exist on other systems MS-DOS (which uses _output in MSC5 with iop as the first parameter) or MPE (HP's proprietary 3000 system) which has an odd stack frame format which includes a count of the number of arguments and flags to indicate if an argument is on the stack. However, from what I remember of the version of C we were using on MPE, the printf family shared common code and varargs definitely existed. So it may be possible to generate portable code, especially if varargs exists on the intended system. I tested the code on our Vax (Sys Vr2) and MS-DOS systems. On a personnel note: Thanks Steve, your include file and usage saved me time I would have wasted using a different method. Regards, Ian Stewartson Data Logic Ltd, Queens House, Greenhill Way, Harrow, Middlesex, HA1 1YR, UK. (Phone) +44 1 863 0383 (Telex) 888103 (Fax) +44 1 861 2010 (Network) gis@datlog.co.uk or ukc!datlog!gis