home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / lang / c / 20235 < prev    next >
Encoding:
Text File  |  1993-01-27  |  8.8 KB  |  230 lines

  1. Path: sparky!uunet!ferkel.ucsb.edu!taco!gatech!swrinde!elroy.jpl.nasa.gov!ames!agate!dog.ee.lbl.gov!horse.ee.lbl.gov!torek
  2. From: torek@horse.ee.lbl.gov (Chris Torek)
  3. Newsgroups: comp.lang.c
  4. Subject: Re: (cast) proposal (WAS: functions as arguments in DEC C)
  5. Date: 26 Jan 1993 17:09:19 GMT
  6. Organization: Lawrence Berkeley Laboratory, Berkeley CA
  7. Lines: 218
  8. Message-ID: <28641@dog.ee.lbl.gov>
  9. References: <1993Jan26.135214.9306@dxcern.cern.ch>
  10. NNTP-Posting-Host: 128.3.112.15
  11.  
  12. In article <1993Jan26.135214.9306@dxcern.cern.ch> burow@dxcern.cern.ch
  13. (Burkhard Burow) writes:
  14. >My application can't [use] '(int (*)())a' because it doesn't know that
  15. >a pointer to a function returning int is expected.
  16. >It just knows that a pointer to function is expected.
  17.  
  18. There "ain't no sech animal" in C.  *EVERY* C function has a type.
  19. That type WILL APPEAR when a pointer to that function is created.  You
  20. can past a new, different label on it, and then paste the original
  21. label back on, but C does not support an `unlabelled' version of such
  22. pointers.  (C *does* have some fairly weak support for `unlabelled'
  23. data pointers, in the form of `void *', but it lacks an equivalent for
  24. function pointers, mainly due to lack of utility.  Old or Classic C
  25. did not have this `unlabelled' type; one simply used `char *'.)
  26.  
  27. >QQQQuestion: Does C provide a cast, other than (void *) to do the following?
  28. >
  29. >#include <stdlib.h>
  30. >
  31. >void f(int a[3][5]) { a[0][0] = 1; return; }
  32. >
  33. >typedef ?????  CAST;
  34. >
  35. >void g(int *b)
  36. >{
  37. >f( (CAST) b);
  38. >}
  39.  
  40. >CONSTRAINT:
  41. >My application, generating the intermediate function g,
  42. >knows the type of the array 'b',     in this case 'int',
  43. >and the number of dimensions of 'b', in this case 2
  44. >but it doesn't know the size of the individual dimensions, here 3 and 5.
  45.  
  46. This is no good:  C does not have the type `array ? of array ? of ...'
  47. (where `?' means `unknown size').  Array sizes may only be omitted
  48. in some very particular situations.  The underlying principle here
  49. is that the size may be omitted only when it is not needed for
  50. address calculations.  Since the size of the `nested' array *is*
  51. needed---the address of a[1][2] in f(), for instance, is calculated
  52. as `base of array plus 1*5 plus 2'---it may not be omitted.
  53.  
  54. If this were not the case, (some) pointers would have to have extra
  55. information.  Such a proposal (or rather, several such proposals) have
  56. been made for the `numerical extentions' version of C.  I personally
  57. favor Ritchie's `fat pointer' method; it has a number of advantages
  58. over GCC's `FORTRAN-like' method.  These are perhaps best done
  59. by illustration:
  60.  
  61.     /* gcc method */
  62.     void f(int m, int n, double a[m][n]) {
  63.         ...
  64.     }
  65.  
  66. Inside the function f, the pointer to which `a's declaration decays has
  67. type `pointer to array n of double', where n is a *variable* rather
  68. than a constant.  This means either that:
  69.  
  70.     The variable n must stay in scope, and its value must not
  71.     be overwritten.
  72.  
  73. or
  74.  
  75.     The original value of n must be saved at function entry
  76.     and somehow invisibly associated with the array `a' within
  77.     the compiler.
  78.  
  79. The scope requirement in the former happens naturally (the compiler can
  80. always `peek out' of an inner scope that defines a new `n'; the only
  81. requirement is that the outer n still exist, which it always will until
  82. `a' is gone).  The constance could be had by forcing n to be `const
  83. int' instead of just `int', but this is not currently required in gcc.
  84. The latter version seems more sensible, but still gives me a creepy
  85. feeling of `unnecessarily complicated and error-prone machinery'.  (One
  86. could, for instance, pass the wrong value for n, with disastrous
  87. consequences.)
  88.  
  89.     /* fat pointer method */
  90.     /* I have arbitrarily used my `?' syntax here */
  91.     void f(double a[][?]) {
  92.         ...
  93.     }
  94.  
  95. In this case the pointer has type `pointer to array passed-size of
  96. double'.  This size is accessible as `sizeof a[0] / sizeof double' or
  97. `sizeof *a / sizeof double'.  Since it does not have a variable name,
  98. however, one cannot expect it to `act like' a variable.  These `fat
  99. pointers' are really `dope vectors' in disguise.
  100.  
  101. Note that both methods require additional compiler machinery, over and
  102. above what is now required to implement ANSI C.
  103.  
  104. >QQQuestion/PPProposal:
  105. >Why doesn't C allow 'incomplete casting' in order to allow one to best match
  106. >the prototyping of the arguments of functions?
  107.  
  108. Mainly due to the additional machinery.  Aside from gcc's method, none
  109. of these have been widely implemented, so there was no experience to
  110. draw on when making the ANSI C standard.
  111.  
  112. >i)   pointer to function >cast> pointer to function returning void or any type:
  113. >     ((*)())
  114.  
  115. Remember that C can be implemented on machines that are nearly
  116. arbitrarily perverse.  For instance, consider the following:
  117.  
  118.     char = 8 bits
  119.     short = 16 bits
  120.     int = 32 bits
  121.     long = 64 bits
  122.  
  123.     pointer to char = 96 bits (used via `byte pointer' registers bp0..bp3)
  124.     pointer to short = 64 bits (used via `subaddress' registers sp0..sp2)
  125.     pointer to int = 64 bits (also used via subaddress registers)
  126.     pointer to long = 32 bits (used via `address' registers ap0..ap6)
  127.  
  128.     pointer to function returning void = 64 bits (address of procedure)
  129.     pointer to function returning int = 96 bits
  130.         (address of procedure + pointer to value slot)
  131.     pointer to function returning char = 160 bits
  132.         (address of procedure + pointer to value slot)
  133.  
  134. On such a machine, `void *' (a `pointer to any data') must have the
  135. `longest' object pointer type, in this case, 96 bits.  It would be
  136. possible to define a `pointer to any_function' type; again, this would
  137. have to have the largest size, or 160 bits.
  138.  
  139. As it turns out, in practise, ANSI C already requires the compiler for
  140. this machine to make *all* function pointers 160 bits, or else come up
  141. with some method for `reconsitituting' the 160-bit value when a
  142. `pointer to function returning char' is turned temporarily into a
  143. `pointer to function returning void'.  (In practise again, the most
  144. convenient way to implement C on such a machine is typically to
  145. implement all function pointers via dope vectors or handles, but
  146. never mind that.)
  147.  
  148. Since the value slot pointer does not exist for `procedures' (functions
  149. returning void), but does for others, it is clearly a mistake to call
  150. functions via procedure pointers and vice versa.  Moreover, since the
  151. value slot pointer is different sizes for different return types, the
  152. `actual underlying type' must be used.  Otherwise the value slot
  153. pointer will be passed in the wrong register (bp3 instead of ap6,
  154. for instance).
  155.  
  156.             *    *    *    *
  157.  
  158. Now in fact, the original problem brought up seems to be `calling
  159. between C and FORTRAN'.  As soon as we bring other languages into this,
  160. the issue gets *much* cloudier.  The C standard says nothing about
  161. interlanguage calling.  All we can say for certain is that, in ANSI
  162. C, code like the following is entirely legal.
  163.  
  164.     #include <math.h>
  165.     #include <stdio.h>
  166.     #include <stdlib.h>
  167.  
  168.     typedef void (*gfp)(void);    /* generic function pointer */
  169.     typedef void *gdp;        /* generic data pointer */
  170.  
  171.     #define F_INT_RET_INT    0    /* e.g., abs */
  172.     #define    F_DBL_RET_DBL    1    /* e.g., sin, cos */
  173.  
  174.     void silly(gfp fn, gdp val, int realtype) {
  175.         switch (realtype) {
  176.  
  177.         case F_INT_RET_INT:
  178.             *(int *)val = ((int (*)(int))fn)(-3);
  179.             return;
  180.  
  181.         case F_DBL_RET_DBL:
  182.             *(double *)val = ((double (*)(double))fn)
  183.                 (.78539816339744830961);
  184.             return;
  185.  
  186.         default:
  187.             (void)fprintf(stderr, "panic: silly: badrealtype\n");
  188.             abort();
  189.         }
  190.     }
  191.  
  192.     int main(void) {
  193.         double root2over2;
  194.         int three;
  195.  
  196.         /*
  197.          * No casts are required for the 2nd arg as a prototype
  198.          * is in scope, but we will use one anyway.
  199.          */
  200.         silly((gfp)abs, (void *)&three, F_INT_RET_INT);
  201.         silly((gfp)sin, (void *)&root2over2, F_DBL_RET_DBL);
  202.         (void)printf("this should be 3: %d\n", three);
  203.         (void)printf("this should be .7071...: %.10f\n", root2over2);
  204.         exit(EXIT_SUCCESS);
  205.     }
  206.  
  207. The following, however, is wrong:
  208.  
  209.         silly((gfp)abs, (void *)&three, F_DBL_RET_DBL);
  210.  
  211. The call to silly() is correct, but silly() will then attempt to
  212. call abs() through a `pointer to function (double) returning double'.
  213. The result is undefined.
  214.  
  215. If one is calling a FORTRAN function, rather than `silly', one must
  216. provide to that function whatever type it expects.  It is rather
  217. bizarre to ask a C standard to say what FORTRAN must expect, or vice
  218. versa.  If the FORTRAN module expects a `pointer to function (double)
  219. returning double', passing `sin' directly will work.  If it expects
  220. a `pointer to function (void) returning void', and internally converts
  221. it to `pointer to function (double) returning double', then passing
  222. `(gfp)sin' will work.  But we just cannot say *what* it expects---after
  223. all, this is comp.lang.c, not comp.lang.interlanguage.  Indeed, it
  224. may even expect a type that does not *exist* in C.  Your only option
  225. is to look in your vendor's documentation and see if they tell you
  226. what to do.
  227. -- 
  228. In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 510 486 5427)
  229. Berkeley, CA        Domain:    torek@ee.lbl.gov
  230.