ijk@cbnewsh.ATT.COM (ihor.j.kinal) (03/01/90)
I've set up an array of 50 functions, to be called with one standard argument, and my function call looks like this [which particular function to be called is determined by the action_ind. action_tbl [ action_ind ] ( one_arg ) where the one_arg is a pointer to a particular type. Now, I want to add some functions that will have two arguments. The question is, how do I express the call to that function both simply and portably? Obviously, I could provide a list of arguments for each function in my table, and I could test if an arg two is present, then I call action_tbl [ action_ind ] ( one_arg, two_arg ) and otherwise, call the old way. This seems incredibly clumsy. Alternately, I could call EVERY function with two arguments, with the second being null for the actual one-arg functions. Note that I don't want to go back to the source and add a dummy argument to all the old functions. Given that my machine passes the first 4 arguments as registers, I believe that this should work - however, it strikes me as potentially non-portable on some stack machines. What I really want is to construct an arg list and pass that to my function call, but I can't figure how to do that. Any help will be appreciated, Ihor Kinal att!cbnewsh!ijk
BRL102@psuvm.psu.edu (Ben Liblit) (03/06/90)
In article <8553@cbnewsh.ATT.COM>, ijk@cbnewsh.ATT.COM (ihor.j.kinal) says: > >I've set up an array of 50 functions, to be called with one standard >argument, and my function call looks like this [which particular >function to be called is determined by the action_ind. > > action_tbl [ action_ind ] ( one_arg ) > > where the one_arg is a pointer to a particular type. > >Now, I want to add some functions that will have two arguments. The best solution I can think of is to stick with a single argument, but make one_arg a void pointer. A particular function will typecast one_arg to a pointer to a structure containing the actual parameters to be used. Make any sense? Ben Liblit BRL102 @ psuvm.bitnet -- BRL102 @ psuvm.psu.edu "Fais que tes reves soient plus longs que la nuit."
CMH117@psuvm.psu.edu (Charles Hannum) (03/07/90)
In article <90065.102500BRL102@psuvm.psu.edu>, Ben Liblit <BRL102@psuvm.psu.edu> says: > >In article <8553@cbnewsh.ATT.COM>, ijk@cbnewsh.ATT.COM (ihor.j.kinal) says: >> >>I've set up an array of 50 functions, to be called with one standard >>argument, and my function call looks like this [which particular >>function to be called is determined by the action_ind. >> >> action_tbl [ action_ind ] ( one_arg ) >> >> where the one_arg is a pointer to a particular type. >> >>Now, I want to add some functions that will have two arguments. > >The best solution I can think of is to stick with a single argument, but make >one_arg a void pointer. A particular function will typecast one_arg to a >pointer to a structure containing the actual parameters to be used. Make any >sense? ... or you could use a variable argument list ... Virtually, - Charles Martin Hannum II "Klein bottle for sale ... inquire within." (That's Charles to you!) "To life immortal!" cmh117@psuvm.{bitnet,psu.edu} "No noozzzz izzz netzzzsnoozzzzz..." c9h@psuecl.{bitnet,psu.edu} "Mem'ry, all alone in the moonlight ..."
chris@mimsy.umd.edu (Chris Torek) (03/07/90)
In article <8553@cbnewsh.ATT.COM> ijk@cbnewsh.ATT.COM (ihor.j.kinal) writes: [existing calls and functions of the form] > action_tbl [ action_ind ] ( one_arg ) >Now, I want to add some functions that will have two arguments. >The question is, how do I express the call to that function >both simply and portably? There is no simple, portable method. Indeed, even the obvious approach: >Obviously, I could provide a list of arguments for each function >in my table, and I could test if an arg two is present, then I call > > action_tbl [ action_ind ] ( one_arg, two_arg ) > >and otherwise, call the old way. This seems incredibly clumsy. (which is indeed rather clumsy) is not truly portable, not under ANSI C. The reason is that `action_tbl' must have one of the two types sometype (*action_tbl[SIZE])(type1 arg1); or sometype (*action_tbl[SIZE])(type1 arg1, type2 arg2); It cannot have both, save by being a union. Fortunately, all pointers to functions have the same underlying size, so you can get away with using one of these types, and applying casts to force the other type to match. That is: int f0(char *); /* a one-arg fn */ int f1(char *); /* another one-arg fn */ int f2(int *, int *); /* a two-arg fn */ int (*action_tbl[3])(char *) = { f0, f1, (int (*)(char *))f2 }; ... if (i < 2) /* calling f0 or f1 */ ret = (*action_tbl[i])(char_star_arg); else /* calling f2 */ ret = (*(int (*)(int *, int *))action_tbl[i])( int_star_arg_1, int_star_arg_2); >Alternately, I could call EVERY function with two arguments, >with the second being null for the actual one-arg functions. This requires adding a dummy argument to all the one-arg functions . . . >Note that I don't want to go back to the source and add a dummy argument >to all the old functions. . . . as you supposed. >What I really want is to construct an arg list and pass that to my >function call, but I can't figure how to do that. There is no such option. It would be nice, but it does not exist. As an alternative to the example above, the table can be made of a union, or a structure containing a union. Unfortunately, it then cannot be initialised usefully (at least, not without using casts). One alternative is to require that all functions take one argument always. Functions that could use two must instead receive the address of a structure containing the two desired arguments. For instance: typedef char *pointer; /* void * in ANSI C */ int f2(pointer arg0) { struct a { int *a1, *a2; } *args = (struct a *)arg0; ... work with args->a1 and args->a2 ... } The latter is quite similar to the way the Unix kernel handles arguments to system calls. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris
schaut@cat9.cs.wisc.edu (Rick Schaut) (03/08/90)
In article <22954@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes: | In article <8553@cbnewsh.ATT.COM> ijk@cbnewsh.ATT.COM (ihor.j.kinal) writes: | >Obviously, I could provide a list of arguments for each function | >in my table, and I could test if an arg two is present, then I call | > | > action_tbl [ action_ind ] ( one_arg, two_arg ) | > | >and otherwise, call the old way. This seems incredibly clumsy. | | (which is indeed rather clumsy) is not truly portable, not under ANSI C. | The reason is that `action_tbl' must have one of the two types | | sometype (*action_tbl[SIZE])(type1 arg1); | | or | | sometype (*action_tbl[SIZE])(type1 arg1, type2 arg2); | | It cannot have both, save by being a union. Fortunately, all pointers | to functions have the same underlying size, so you can get away with | using one of these types, and applying casts to force the other type | to match. That is: [One option for implementing this deleted.] If all you want is a way to prototype the functions, sometype (*action_tbl)[SIZE](type1 arg1,...); will do nicely. This does sacrifice type checking on the second argument, but I get the impression this is not an important consideration. | >What I really want is to construct an arg list and pass that to my | >function call, but I can't figure how to do that. | | There is no such option. It would be nice, but it does not exist. If the compiler is ANSI compliant, then there is, indeed, a portable and standard way to implement this. In fact, the ANSI version of printf is implemented using a variable-length argument list. For further reference, see K&R, 2nd Ed. Sect. 7.3 (page 155). -- Rick (schaut@garfield.cs.wisc.edu) "I'm a theory geek; we use Turing machines!"--Gary Lewandowski
chris@mimsy.umd.edu (Chris Torek) (03/08/90)
>>In article <8553@cbnewsh.ATT.COM> ijk@cbnewsh.ATT.COM (ihor.j.kinal) asks about ways to call functions (via a table of pointers) with differing (but always fixed) argument lists. >In article <22954@mimsy.umd.edu> I wrote about how this cannot be done in ANSI C. In article <4410@daffy.cs.wisc.edu> schaut@cat9.cs.wisc.edu (Rick Schaut) writes: >If all you want is a way to prototype the functions, > sometype (*action_tbl)[SIZE](type1 arg1,...); >will do nicely. This does sacrifice type checking on the second argument, >but I get the impression this is not an important consideration. This does more than sacrifice type checking on second and further arguments: It also requires that every function called via the table be changed. In particular, int foo(char *p, ...) { <code using p> } is not type-compatible with int bar(char *p) { <same code using p> } Indeed, these are the most likely to differ in current implementations. That is, given int (*p)(); /* old-style declaration */ extern int fn(char *, double); p = fn; /* set p to point to some function returning int */ (*p)("foo", 2.71828); the compiler will probably generate code that works. However, given int (*p)(); /* old-style declaration, again */ extern int fn(char *, ...); p = fn; (*p)("foo", 2.71828); the compiler may well generate code that fails. [ijk] >>>What I really want is to construct an arg list and pass that to my >>>function call, but I can't figure how to do that. [chris] >>There is no such option. It would be nice, but it does not exist. >If the compiler is ANSI compliant, then there is, indeed, a portable and >standard way to implement this. In fact, the ANSI version of printf is >implemented using a variable-length argument list. For further reference, >see K&R, 2nd Ed. Sect. 7.3 (page 155). This is not the same thing: this is passing a variable argument list to a function known to take a variable argument list; what Ihor Kinal described is constructing a fixed argument list to pass to a function known to take a fixed argument list. That is, in some C-like language with this augmentation, we might write: fa_start(&argmagic); switch (tab[i].arg) { case NO_ARGS: break; case ONE_INT_ARG: fa_addarg(&argmagic, arg1, int); break; case ONE_INT_ARG_THEN_ONE_DOUBLE_ARG: fa_addarg(&argmagic, arg1, int); fa_addarg(&argmagic, arg2, double); break; default: panic("unknown arg type: tab[%d].arg = %d", i, tab[i].arg); /* NOTREACHED */ } fa_invoke(tab[i].function, &argmagic); fa_end(&argmagic); Summary: a prototype with a `...' is not compatible with a declaration without a `...', and vice versa. To use a variable argument list requires changing all the existing code (something ijk asked to avoid). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris