roy@bonzo.sts.COM (10/04/89)
Here are a couple questions that come up in conjunction with using the 'varargs' series of function calls. When calling the va_arg() function, the 2nd parameter is supposed to be simply a type, such as int, char, char *, etc. So, the first question is, how does it know what type you specified? You're not specifying a variable - it's only a type. I really am curious about what the semantics to the compiler would be ... The second question is in conjunction with how to declare certain types. Things like 'int' and 'char *' are a piece of cake, but how about a good, general declaration for a function? '(int *())' and '(int ())' were two tries at declaring a general function that returns an 'int', but they didn't work. Are you stuck with something like '(int (*foo)())', where 'foo' is a particular function or is there a better way to do this? The only thing we've tried that works is just to declare the type as 'char *'. But that's ugly and non-portable. Any other ideas? ================================================================== Roy Bixler | UUCP: ...!{claris,sun}!sts!roy | Semiconductor Test Solutions | Internet: roy@bonzo.sts.COM | 4101 Burton Dr. | phones: (408) 727-2885 x132 (work)| Santa Clara, CA 95054 | (408) 289-1035 (home)| ==================================================================
chris@mimsy.UUCP (Chris Torek) (10/04/89)
In article <80100001@bonzo> roy@bonzo.sts.COM writes: >Here are a couple questions that come up in conjunction with using the >'varargs' series of function calls. When calling the va_arg() >function, the 2nd parameter is supposed to be simply a type .... va_arg is not a function. va_arg is a macro or (in some compilers) a `magic cookie' (in ANSI-conformant compilers, once the ANSI C standard is a standard, va_arg will have to be a macro that is defined as a magic cookie in these compilers). >... how does it know what type you specified? The usual definition of va_arg for conventional stack machines is something like #define va_arg(ap, type) (((type *)(ap += sizeof(type)))[-1]) Note in particular that the `type' argument must produce a new type when suffixed with `*', so that `int' and `double' are legal, but `void (*)(void)' is not. >The second question is in conjunction with how to declare certain types. >Things like 'int' and 'char *' are a piece of cake, but how about a good, >general declaration for a function? If you are trying to write `ANSI C' (which, again, does not yet exist; we are all just assuming that it will work the way the proposed standard describes), the short answer is `there is none'. Every function pointer, however, `fits in' every other function pointer, so you can obtain a `pointer to function of no arguments returning int' and then cast it to `pointer to function of one ``char *'' argument returning int' and then call through the result: foo(sometype firstarg, ...) { /* * A typedef is required so that we can append `*' to * get `pointer to pointer to function (args) returning ...' */ typedef int (*fn)(void); fn x; va_list ap; int first = 1; va_start(ap, firstarg); for (;;) { x = va_arg(ap, fn); if (x == NULL) break; if (first) { ((int (*)(sometype))fn)(firstarg); first = 0; } else fn(); } va_end(ap); } If all the `fn's will have the same (fixed) set of arguments, you can simply typedef int (*fn)(type1, type2, type3); and leave out all the casts. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/04/89)
In article <80100001@bonzo>, roy@bonzo.sts.COM writes: | | Here are a couple questions that come up in conjunction with using the | 'varargs' series of function calls. When calling the va_arg() | function, the 2nd parameter is supposed to be simply a type, such as ^^^^^^^^^ nope, macro. Does that make it clear? It does something like this: #define va_arg(list,mode) list+=sizeof(mode),((mode *)list)[-1] or somthing along those lines. | '(int *())' and '(int ())' were two tries at declaring a general function If I understand the question, you want (int(*)()) -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon
gwyn@smoke.BRL.MIL (Doug Gwyn) (10/05/89)
In article <80100001@bonzo> roy@bonzo.sts.COM writes: >When calling the va_arg() function, the 2nd parameter is supposed to be >simply a type, such as int, char, char *, etc. So, the first question >is, how does it know what type you specified? It doesn't work with all types, only those for which appending "*" produces a type that is a valid pointer to the argument type. Of course the va_arg() MACRO (not function) does this by straightforward macro expansion of the argument using a template where the expanded argument is followed by a "*". Sometimes you need to provide a typedef name, if the type is complex enough. >'(int *())' and '(int ())' were two tries at declaring a general function >that returns an 'int', but they didn't work. int (*)() is a valid type for a pointer to function (with unspecified argument information) returning an int value.
troy@mr_plod.cbme.unsw.oz (Troy Rollo) (10/05/89)
From article <80100001@bonzo>, by roy@bonzo.sts.COM: roy> cake, but how about a good, general declaration for a function? roy> '(int *())' and '(int ())' were two tries at declaring a general function roy> that returns an 'int', but they didn't work. Are you stuck with roy> something like '(int (*foo)())', where 'foo' is a particular function Strictly, there's no legal way of doing this..... Hang on, yes there is: typedef int (*function)(); Then when you're putting it in the code, user (function) as your type cast. This isn't really on the edge of the language specs.... although I ran into a question last night which was... somebody wanted to define a pair of structures which were initialised with pointers to eachother. Fine, except that one hasn't been defined - no address because no space is allocated, and the compiler doesn't have the faintest idea what you're on about until later, when you declare the second structure. The solution was to effect a forward declaration by using the extern keyword. This causes the problem to be passed off to the linker, which resolves the external reference - from the same file! struct a_struct { void *next; int value; }; struct b_struct { struct a_struct *next; int value; }; extern struct b_struct struc2; struct a_struct struc1 = { &struc2, 0 }; struct b_struct struc2 = { &struc1, 0 }; ___________________________________________________________ troy@mr_plod.cbme.unsw.oz.au Make our greenies useful! The Resident Fascist Put them in the army!
chris@mimsy.UUCP (Chris Torek) (10/05/89)
In article <19971@mimsy.UUCP> I wrote: [most lines deleted] > typedef int (*fn)(void); > fn x; [stuff missing here] > x = va_arg(ap, fn); [and here] > ((int (*)(sometype))fn)(firstarg); [and here] > fn(); Oops, the last two should be calls through `x', not `fn'. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris
Tim_CDC_Roberts@cup.portal.com (10/06/89)
Re: How do I write a cast to a function pointer etc.? Within the past few months, one of the gurus here posted a quick rule for figuring out how to write a cast, which I made myself read over and over until I memorized it. To make a cast for a given simple or complex type, first write a declaration of some "thing" of that type: int * (* func)(); func is ptr to function returning ptr to int Then, remove the name of the "thing" and surround the declaration with parentheses. This becomes the cast: (int * (*)()) x x is cast to ptr to function returning ptr to int This method is so simple and elegant (and not intuitively obvious to a beginning-to-intermediate C programmer), that it deserves to be in that list of "Commonly asked C Questions", whenever someone gets around to putting it together. Tim_CDC_Roberts@cup.portal.com | Control Data... ...!sun!portal!cup.portal.com!tim_cdc_roberts | ...or it will control you.
roy@bonzo.sts.COM (10/06/89)
First off, I'd like to thank everyone for the responses. At least, now I have it straight that va_arg is actually a macro, which is the only thing that makes sense. The 2nd question still holds though. It looks like the 'typedef' method may be the only way. I've tried (as suggested by gwyn@smoke.BRL.MIL) '((void (*)())' and '(void) (*)()' and they didn't work either. I'm not sure if this is a quirk in the Sun C compiler or what. ================================================================== Roy Bixler | UUCP: ...!{claris,sun}!sts!roy | Semiconductor Test Solutions | Internet: roy@bonzo.sts.COM | 4101 Burton Dr. | phones: (408) 727-2885 x132 (work)| Santa Clara, CA 95054 | (408) 289-1035 (home)| ==================================================================
dfp@cbnewsl.ATT.COM (david.f.prosser) (10/07/89)
In article <453@usage.csd.unsw.oz> troy@mr_plod.cbme.unsw.oz writes: >This isn't really on the edge of the language specs.... although I ran into >a question last night which was... somebody wanted to define a pair of >structures which were initialised with pointers to eachother. Fine, except >that one hasn't been defined - no address because no space is allocated, and >the compiler doesn't have the faintest idea what you're on about until later, >when you declare the second structure. The solution was to effect a forward >declaration by using the extern keyword. This causes the problem to be passed >off to the linker, which resolves the external reference - from the same file! >struct a_struct { > void *next; > int value; >}; >struct b_struct { > struct a_struct *next; > int value; >}; >extern struct b_struct struc2; >struct a_struct struc1 = { &struc2, 0 }; >struct b_struct struc2 = { &struc1, 0 }; While this code may be required with certain old compilers, the following is valid ANSI C: struct s1 { struct s2 *s2p; /*...*/ }; struct s2 { struct s1 *s1p; /*...*/ }; struct s1 one; struct s2 two = { &one, /*...*/ }; struct s1 one = { &two, /*...*/ }; The first two lines are from page 64, lines 28 and 29. Note that the use of "void *" in the quoted article is neither necessary nor appropriate. The first declaration of "one" is a tentative definition. The following declaration of "one" is the definition. "extern" is unnecessary. Dave Prosser ...not an official X3J11 answer...
jeenglis@nunki.usc.edu (Joe English) (10/07/89)
troy@mr_plod.cbme.unsw.oz writes: >This isn't really on the edge of the language specs.... although I ran into >a question last night which was... somebody wanted to define a pair of >structures which were initialised with pointers to eachother. [...] >struct a_struct { > void *next; > int value; >}; > >struct b_struct { > struct a_struct *next; > int value; >}; Except you probably shouldn't use void *. I just tried this, and both gcc and cc (SunOS) accepted it: struct foo; /* forward declaration -- unnecessary, though */ struct bar { struct foo *foop; /* this is OK. */ }; struct foo { struct bar *barp; /* OK, struct bar seen alrerady */ struct diddle *diddlep; /* OK, struct diddle defined later */ struct qwerty *qwertyp; /* OK, struct qwerty *never* defined */ }; struct diddle { int asdf; }; Presumably the compiler would complain if I tried to use a foo::qwertyp before it had seen the definition of struct qwerty, but the rest worked just fine. (BTW, is this behaviour specified in the standard?) --Joe English jeenglis@nunki.usc.edu
gwyn@smoke.BRL.MIL (Doug Gwyn) (10/07/89)
In article <457@usage.csd.unsw.oz> troy@mr_plod.cbme.unsw.oz writes:
-From article <789@crdos1.crd.ge.COM>, by davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr):
-davidsen> If I understand the question, you want (int(*)())
-Nope - bracketing only works when you have something to group with. that will
-produce exactly the same results as (int *())
Wrong.
troy@mr_plod.cbme.unsw.oz (Troy Rollo) (10/09/89)
From article <789@crdos1.crd.ge.COM>, by davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr): davidsen> If I understand the question, you want (int(*)()) Nope - bracketing only works when you have something to group with. that will produce exactly the same results as (int *()) ___________________________________________________________ troy@mr_plod.cbme.unsw.oz.au Make our greenies useful! The Resident Fascist Put them in the army!
chris@mimsy.UUCP (Chris Torek) (10/10/89)
[desired: a cast to `pointer to function returning int'] >>If I understand the question, you want (int(*)()) [this is correct] In article <457@usage.csd.unsw.oz> troy@mr_plod.cbme.unsw.oz (Troy Rollo) writes: >Nope - bracketing only works when you have something to group with. that will >produce exactly the same results as (int *()) This is wrong. There is something to group here, namely the empty name. (int *()) is a cast to `function returning pointer to int', which is not a legal type for a C expression. (int (*)()) binds the asterisk to the empty name, yeilding a cast to `pointer to function of unknown arguments returning int'. This may be easier to see when considering a variable declaration: int i; /* Int */ int *pi; /* Pointer to Int */ int fi(); /* Function returning Int */ int *fpi(); /* Function returning Pointer to Int */ int (*pfi)(); /* Pointer to Function returning Int */ int *(*pfpi)(); /* Pointer to Function returning Pointer to Int */ To turn each of the above into casts, drop the name and surround the whole thing with parentheses. A cast whose `top level' type (first word in the expansion above) is `function returning ...' is illegal. The legal casts are thus: (int) (int *) [cannot cast to Func ret Int] [cannot cast to Func ret Ptr to Int] (int (*)()) (int *(*)()) One can use the freely-available (possibly public domain) `cdecl' program to come up with things like % cdecl [nb: the \-newlines here are for the purpose of posting; cdecl takes a single input line] declare foo as pointer to function returning pointer to function \ returning pointer to array 3 of pointer to function returning \ pointer to array 4 of array 2 of pointer to pointer to function \ returning pointer to array 5 of pointer to pointer to char char **(*(**(*(*(*(*(*foo)())())[3])())[4][2])())[5] To turn the above into a cast, remove the name `foo' and add (): (char **(*(**(*(*(*(*(*)())())[3])())[4][2])())[5]) Of course, in ANSI C, one should include the types of the arguments to each of the functions.... -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris
peterson@lyle.UUCP (James L. Peterson/1000000) (10/10/89)
We have run into a problem that looks like it should be solvable
with var_args, but after an attempt feel that something is missing.
We have a number of routines which call other routines. We want
to interpose a general routine between each caller and called
routine. The parameter lists always have the same first element,
followed by other parameters as appropriate for the called routine.
We want the one general routine to work with the first parameter
and then pass the entire parameter list that it got on to the
called routine.
The interposed general routine does not know how many or what
types of parameters it will get, so varargs seems appropriate.
We can get the first parameter out of the list by using va_arg.
Based on this, and other info, we can compute a pointer to the
routine that was being called (realfunc). We then want to call
the realfunc with the same parameter list that we got (whatever it
was). Something like:
general(va_alist)
va_dcl
{
va_start(argp);
first = va_arg(argp,type);
... do stuff with first ...
(*realfunc)(first, rest);
}
How do we refer to the "rest of the argument list". Or since we just
want to send the same thing out that we got in, is there a way to make
the (*realfunc) call with the incoming va_alist ?
--
James L. Peterson
IBM Advanced Workstations Div. !'s: cs.utexas.edu!ibmaus!peterson
11400 Burnet Road, D75/996 @'s: @CS.UTEXAS.EDU:peterson@ibmaus.uucp
Austin, Texas 78758-2603 !&@: ibmaus!peterson@CS.UTEXAS.EDU
(512) 823-5169 vnet: JPETER at AUSVM6
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/11/89)
In article <457@usage.csd.unsw.oz>, troy@mr_plod.cbme.unsw.oz (Troy Rollo) writes: | From article <789@crdos1.crd.ge.COM>, by davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr): | | davidsen> If I understand the question, you want (int(*)()) | | Nope - bracketing only works when you have something to group with. that will | produce exactly the same results as (int *()) In the original question some aspect of varargs was being questioned, in which the argument in question is used as a cast. This is how a cast works: (int (*)()) cast X into pointer to function returning int (int *()) cast X into function returning pointer to int I realize that the macro may not be identical on all systems, but I *thought* that's what the std said, a type, formatted to be used as a cast. Used *as I indicated* the expressions do not have the same effect at all and I'm not sure that the 2nd is legal, casting something to be a function??? -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon
henry@utzoo.uucp (Henry Spencer) (10/14/89)
In article <2792@lyle.UUCP> peterson@lyle.UUCP (James L. Peterson/1000000) writes: >We have a number of routines which call other routines. We want >to interpose a general routine between each caller and called >routine. The parameter lists always have the same first element, >followed by other parameters as appropriate for the called routine. >We want the one general routine to work with the first parameter >and then pass the entire parameter list that it got on to the >called routine. I'm afraid the answer is: "you can't do this in portable C". The C varargs facility is *not* a general set of primitives for manipulating argument lists; it is a special-purpose hack to legitimize a handful of special cases like printf. What you want to do is beyond its powers. -- A bit of tolerance is worth a | Henry Spencer at U of Toronto Zoology megabyte of flaming. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu
gwyn@smoke.BRL.MIL (Doug Gwyn) (10/14/89)
In article <2792@lyle.UUCP> peterson@lyle.UUCP (James L. Peterson/1000000) writes: >How do we refer to the "rest of the argument list". You have to pass the va_list you've va_start()ed (i.e. argp). The called function now gets an argument fo a definite type (va_list), not a variable list of arguments. It must use the va_arg() macro to pick off the remaining arguments.
tony@oha.UUCP (Tony Olekshy) (10/15/89)
In message <20077@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes: > > One can use the freely-available (possibly public domain) `cdecl' > program to come up with things like [...] > > char **(*(**(*(*(*(*(*foo)())())[3])())[4][2])())[5] > > Of course, in ANSI C, one should include the types of the arguments to > each of the functions.... FYI: (echo `cat` | cdecl) << \_end_ declare foo as pointer to function (a,b) returning pointer to function (c, d) returning pointer to array 3 of pointer to function (e, f) returning pointer to array 4 of array 2 of pointer to pointer to function (g, h) returning pointer to array 5 of pointer to pointer to char _end_ Generates: char **(*(**(*(*(*(*(*foo)(a, b))(c, d))[3])(e, f))[4][2])(g, h))[5] So, isn't: typedef int a, b, c, d, e, f; (char **(*(**(*(*(*(*(*)(a, b))(c, d))[3])(e, f))[4][2])(g, h))[5])x; ok? -- Yours, etc., Tony Olekshy (...!alberta!oha!tony or tony@oha.UUCP).