home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!ferkel.ucsb.edu!taco!gatech!swrinde!elroy.jpl.nasa.gov!ames!agate!dog.ee.lbl.gov!horse.ee.lbl.gov!torek
- From: torek@horse.ee.lbl.gov (Chris Torek)
- Newsgroups: comp.lang.c
- Subject: Re: (cast) proposal (WAS: functions as arguments in DEC C)
- Date: 26 Jan 1993 17:09:19 GMT
- Organization: Lawrence Berkeley Laboratory, Berkeley CA
- Lines: 218
- Message-ID: <28641@dog.ee.lbl.gov>
- References: <1993Jan26.135214.9306@dxcern.cern.ch>
- NNTP-Posting-Host: 128.3.112.15
-
- In article <1993Jan26.135214.9306@dxcern.cern.ch> burow@dxcern.cern.ch
- (Burkhard Burow) writes:
- >My application can't [use] '(int (*)())a' because it doesn't know that
- >a pointer to a function returning int is expected.
- >It just knows that a pointer to function is expected.
-
- There "ain't no sech animal" in C. *EVERY* C function has a type.
- That type WILL APPEAR when a pointer to that function is created. You
- can past a new, different label on it, and then paste the original
- label back on, but C does not support an `unlabelled' version of such
- pointers. (C *does* have some fairly weak support for `unlabelled'
- data pointers, in the form of `void *', but it lacks an equivalent for
- function pointers, mainly due to lack of utility. Old or Classic C
- did not have this `unlabelled' type; one simply used `char *'.)
-
- >QQQQuestion: Does C provide a cast, other than (void *) to do the following?
- >
- >#include <stdlib.h>
- >
- >void f(int a[3][5]) { a[0][0] = 1; return; }
- >
- >typedef ????? CAST;
- >
- >void g(int *b)
- >{
- >f( (CAST) b);
- >}
-
- >CONSTRAINT:
- >My application, generating the intermediate function g,
- >knows the type of the array 'b', in this case 'int',
- >and the number of dimensions of 'b', in this case 2
- >but it doesn't know the size of the individual dimensions, here 3 and 5.
-
- This is no good: C does not have the type `array ? of array ? of ...'
- (where `?' means `unknown size'). Array sizes may only be omitted
- in some very particular situations. The underlying principle here
- is that the size may be omitted only when it is not needed for
- address calculations. Since the size of the `nested' array *is*
- needed---the address of a[1][2] in f(), for instance, is calculated
- as `base of array plus 1*5 plus 2'---it may not be omitted.
-
- If this were not the case, (some) pointers would have to have extra
- information. Such a proposal (or rather, several such proposals) have
- been made for the `numerical extentions' version of C. I personally
- favor Ritchie's `fat pointer' method; it has a number of advantages
- over GCC's `FORTRAN-like' method. These are perhaps best done
- by illustration:
-
- /* gcc method */
- void f(int m, int n, double a[m][n]) {
- ...
- }
-
- Inside the function f, the pointer to which `a's declaration decays has
- type `pointer to array n of double', where n is a *variable* rather
- than a constant. This means either that:
-
- The variable n must stay in scope, and its value must not
- be overwritten.
-
- or
-
- The original value of n must be saved at function entry
- and somehow invisibly associated with the array `a' within
- the compiler.
-
- The scope requirement in the former happens naturally (the compiler can
- always `peek out' of an inner scope that defines a new `n'; the only
- requirement is that the outer n still exist, which it always will until
- `a' is gone). The constance could be had by forcing n to be `const
- int' instead of just `int', but this is not currently required in gcc.
- The latter version seems more sensible, but still gives me a creepy
- feeling of `unnecessarily complicated and error-prone machinery'. (One
- could, for instance, pass the wrong value for n, with disastrous
- consequences.)
-
- /* fat pointer method */
- /* I have arbitrarily used my `?' syntax here */
- void f(double a[][?]) {
- ...
- }
-
- In this case the pointer has type `pointer to array passed-size of
- double'. This size is accessible as `sizeof a[0] / sizeof double' or
- `sizeof *a / sizeof double'. Since it does not have a variable name,
- however, one cannot expect it to `act like' a variable. These `fat
- pointers' are really `dope vectors' in disguise.
-
- Note that both methods require additional compiler machinery, over and
- above what is now required to implement ANSI C.
-
- >QQQuestion/PPProposal:
- >Why doesn't C allow 'incomplete casting' in order to allow one to best match
- >the prototyping of the arguments of functions?
-
- Mainly due to the additional machinery. Aside from gcc's method, none
- of these have been widely implemented, so there was no experience to
- draw on when making the ANSI C standard.
-
- >i) pointer to function >cast> pointer to function returning void or any type:
- > ((*)())
-
- Remember that C can be implemented on machines that are nearly
- arbitrarily perverse. For instance, consider the following:
-
- char = 8 bits
- short = 16 bits
- int = 32 bits
- long = 64 bits
-
- pointer to char = 96 bits (used via `byte pointer' registers bp0..bp3)
- pointer to short = 64 bits (used via `subaddress' registers sp0..sp2)
- pointer to int = 64 bits (also used via subaddress registers)
- pointer to long = 32 bits (used via `address' registers ap0..ap6)
-
- pointer to function returning void = 64 bits (address of procedure)
- pointer to function returning int = 96 bits
- (address of procedure + pointer to value slot)
- pointer to function returning char = 160 bits
- (address of procedure + pointer to value slot)
-
- On such a machine, `void *' (a `pointer to any data') must have the
- `longest' object pointer type, in this case, 96 bits. It would be
- possible to define a `pointer to any_function' type; again, this would
- have to have the largest size, or 160 bits.
-
- As it turns out, in practise, ANSI C already requires the compiler for
- this machine to make *all* function pointers 160 bits, or else come up
- with some method for `reconsitituting' the 160-bit value when a
- `pointer to function returning char' is turned temporarily into a
- `pointer to function returning void'. (In practise again, the most
- convenient way to implement C on such a machine is typically to
- implement all function pointers via dope vectors or handles, but
- never mind that.)
-
- Since the value slot pointer does not exist for `procedures' (functions
- returning void), but does for others, it is clearly a mistake to call
- functions via procedure pointers and vice versa. Moreover, since the
- value slot pointer is different sizes for different return types, the
- `actual underlying type' must be used. Otherwise the value slot
- pointer will be passed in the wrong register (bp3 instead of ap6,
- for instance).
-
- * * * *
-
- Now in fact, the original problem brought up seems to be `calling
- between C and FORTRAN'. As soon as we bring other languages into this,
- the issue gets *much* cloudier. The C standard says nothing about
- interlanguage calling. All we can say for certain is that, in ANSI
- C, code like the following is entirely legal.
-
- #include <math.h>
- #include <stdio.h>
- #include <stdlib.h>
-
- typedef void (*gfp)(void); /* generic function pointer */
- typedef void *gdp; /* generic data pointer */
-
- #define F_INT_RET_INT 0 /* e.g., abs */
- #define F_DBL_RET_DBL 1 /* e.g., sin, cos */
-
- void silly(gfp fn, gdp val, int realtype) {
- switch (realtype) {
-
- case F_INT_RET_INT:
- *(int *)val = ((int (*)(int))fn)(-3);
- return;
-
- case F_DBL_RET_DBL:
- *(double *)val = ((double (*)(double))fn)
- (.78539816339744830961);
- return;
-
- default:
- (void)fprintf(stderr, "panic: silly: badrealtype\n");
- abort();
- }
- }
-
- int main(void) {
- double root2over2;
- int three;
-
- /*
- * No casts are required for the 2nd arg as a prototype
- * is in scope, but we will use one anyway.
- */
- silly((gfp)abs, (void *)&three, F_INT_RET_INT);
- silly((gfp)sin, (void *)&root2over2, F_DBL_RET_DBL);
- (void)printf("this should be 3: %d\n", three);
- (void)printf("this should be .7071...: %.10f\n", root2over2);
- exit(EXIT_SUCCESS);
- }
-
- The following, however, is wrong:
-
- silly((gfp)abs, (void *)&three, F_DBL_RET_DBL);
-
- The call to silly() is correct, but silly() will then attempt to
- call abs() through a `pointer to function (double) returning double'.
- The result is undefined.
-
- If one is calling a FORTRAN function, rather than `silly', one must
- provide to that function whatever type it expects. It is rather
- bizarre to ask a C standard to say what FORTRAN must expect, or vice
- versa. If the FORTRAN module expects a `pointer to function (double)
- returning double', passing `sin' directly will work. If it expects
- a `pointer to function (void) returning void', and internally converts
- it to `pointer to function (double) returning double', then passing
- `(gfp)sin' will work. But we just cannot say *what* it expects---after
- all, this is comp.lang.c, not comp.lang.interlanguage. Indeed, it
- may even expect a type that does not *exist* in C. Your only option
- is to look in your vendor's documentation and see if they tell you
- what to do.
- --
- In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 510 486 5427)
- Berkeley, CA Domain: torek@ee.lbl.gov
-