[comp.lang.c] on the fringe of C syntax/semantics

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