home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / comp / lang / fortran / 4425 < prev    next >
Encoding:
Internet Message Format  |  1992-11-23  |  8.5 KB

  1. Path: sparky!uunet!olivea!charnel!sifon!thunder.mcrcim.mcgill.edu!snorkelwacker.mit.edu!ai-lab!life.ai.mit.edu!burley
  2. From: burley@apple-gunkies.gnu.ai.mit.edu (Craig Burley)
  3. Newsgroups: comp.lang.fortran
  4. Subject: Understanding ENTRY and Recursion (was Re: ENTRY in main)
  5. Message-ID: <BURLEY.92Nov23121000@apple-gunkies.gnu.ai.mit.edu>
  6. Date: 23 Nov 92 20:10:00 GMT
  7. References: <By4xpo.FG@iapa.uucp%mailhost.ecn.uoknor.edu>
  8.     <1992Nov23.114807.4734@monu6.cc.monash.edu.au>
  9. Organization: Free Software Foundation 545 Tech Square Cambridge, MA 02139
  10. Lines: 245
  11. NNTP-Posting-Host: apple-gunkies.gnu.ai.mit.edu
  12. In-reply-to: map@hal.maths.monash.edu.au's message of Mon, 23 Nov 1992 11:48:07 GMT
  13.  
  14. In article <1992Nov23.114807.4734@monu6.cc.monash.edu.au> map@hal.maths.monash.edu.au (Michael Page) writes:
  15.  
  16.    ENTRY statements are a form of global GOTO statement and, much as I
  17.    detest and discourage GOTOs, they do have an occasional application,
  18.    in particular for error handling.  I'm not encouraging their use but
  19.    they do seem to have a place in the language.
  20.  
  21. No, ENTRY statements are not a form of a global GOTO statement.  You
  22. misunderstand their use entirely, and any success you've had using them
  23. that way is almost certainly nonportable and/or accidental.
  24.  
  25.    > Also, wouldn't this be considered recursion, since a routine subordinate 
  26.    > to main (effectively) calls main?  
  27.  
  28.    Recursion is already possible (and legal?) in standard FORTRAN by simply 
  29.    having two subroutines which are different only in name that call each 
  30.    other.  I have no idea what this does to the stack, though.
  31.  
  32. No, recursion is not legal in standard FORTRAN.  You can't make it so by
  33. duplicating subroutines and renaming one of them.
  34.  
  35. OK, here is a way to understand what ENTRY means, by way of recoding a
  36. sample use of ENTRY into Fortran code that doesn't use it, but still
  37. preserves all the semantics and meaning:
  38.  
  39.     SUBROUTINE X(ARG1, ARG2)
  40.     COMMON /FOO/ A, B, C
  41.     INTEGER INVOKE
  42.     SAVE INVOKE
  43.     DATA INVOKE/0/
  44. C
  45.     INVOKE = INVOKE + 1
  46.     ...do real stuff #1...
  47.     RETURN
  48. C
  49.     ENTRY Y(ARG2, ARG3)
  50.     INVOKE = INVOKE + 1
  51.     ...do real stuff #2...
  52.     RETURN
  53. C
  54.     ENTRY Z(INVOQ)
  55.     INVOQ = INVOKE
  56.     END
  57.  
  58. Note that X and Y share an argument, ARG2, so it can be referenced,
  59. defined, or undefined when X or Y is called -- but not Z.  Meanwhile,
  60. ARG1 can't be touched in the code path used by a call to Y, nor can
  61. ARG3 be touched by Z's code path.  Although I've shown "real stuff" #s
  62. 1 and 2 as separate things, they can GOTO back and forth between each
  63. other (to share code) as long as these rules are followed.
  64.  
  65. Z just returns in its first argument the total number of times X and Y have
  66. been called prior to Z being called.
  67.  
  68. Note for recursion enthusiasts: once X, Y, or Z is called, the running
  69. program may not call _any_ of X, Y, or Z directly or indirectly until that
  70. first X, Y, or Z returns to its caller.  This means that "real stuff #1"
  71. cannot CALL Z(J), nor can it CALL FROB if FROB does CALL Z(J).
  72.  
  73. Now, here is the above code recoded without ENTRY in an almost entirely
  74. standard way:
  75.  
  76.     SUBROUTINE unique-name(WHICH, ARG1, ARG2, ARG3, INVOQ)
  77.     INTEGER WHICH
  78.     COMMON /FOO/ A, B, C
  79.     INTEGER INVOKE
  80.     SAVE INVOKE
  81.     DATA INVOKE/0/
  82. C
  83.     GOTO (10, 20, 30) WHICH
  84. C
  85. 10    INVOKE = INVOKE + 1
  86.     ...do real stuff #1...
  87.     RETURN
  88. C
  89. 20    INVOKE = INVOKE + 1
  90.     ...do real stuff #2...
  91.     RETURN
  92. C
  93. 30    INVOQ = INVOKE
  94.     END
  95.  
  96.     SUBROUTINE X(ARG1, ARG2)
  97.     CALL unique-name(1, ARG1, ARG2, 0., 0)
  98.     END
  99.  
  100.     SUBROUTINE Y(ARG2, ARG3)
  101.     CALL unique-name(2, 0., ARG2, ARG3, 0)
  102.     END
  103.  
  104.     SUBROUTINE Z(INVOQ)
  105.     CALL unique-name(3, 0., 0., 0., INVOQ)
  106.     END
  107.  
  108. The only nonstandard thing is "unique-name", which the translation must
  109. substitute with a name that is unique across the entire program (easy
  110. for compilers, usually -- GNU Fortran just makes a name unique across
  111. the source file and doesn't make it global, which achieves the same thing).
  112.  
  113. It might look wrong for X and Y to pass 0 for INVOQ, since INVOQ is modified
  114. by the unique-name procedure, but it isn't -- the variable is only modified
  115. in the case where Z calls unique-name, else the _original_ example was as
  116. nonconforming as this recoding.
  117.  
  118. f2c handles ENTRY similar to the above method, though since it has the
  119. luxury of translating into C instead of Fortran, it can do some things more
  120. simply (e.g. pass NULL instead of 0. for missing arguments, especially nice
  121. when the missing argument is a large array and, for pure standards
  122. conformance, I think that'd mean a space-wasting unused array would have
  123. to be passed to make the code valid Fortran, while NULL is suitable for
  124. valid C).
  125.  
  126. Recursion is another story entirely.  If you've been doing something
  127. like
  128.  
  129.     SUBROUTINE X(ARG1, ARG2, ARG3)
  130.     ...
  131.     ENTRY XERROR
  132.     PRINT *,'Error in X'
  133.     ARG1 = 0.
  134.     END
  135.  
  136. and calling XERROR when an error is detected in X or by any of the
  137. procedures it calls, you are attempting recursion, and you're also asking
  138. from trouble in other ways (even if X isn't active).
  139.  
  140. The recursion problem with the above is that the compiler might well
  141. have made lots of internal (invisible) data structures static, meaning
  142. that even the PRINT statement might not work properly on all systems
  143. (even though it might on yours).
  144.  
  145. The other problem is that XERROR doesn't take ARG1 as an argument, so the
  146. assignment is non-conforming.  If your machine happens to, when X is
  147. active at the point XERROR is invoked, actually set X's ARG1 to 0. as a
  148. result of the assignment, you're lucky or incredibly knowledgable about writing
  149. very nonportable code for your particular machine.  But you're not using
  150. ENTRY correctly from a standard-conformance point of view.
  151.  
  152. Instead, you have to do something like:
  153.  
  154.     SUBROUTINE X(ARG1, ARG2, ARG3)
  155.     ...
  156. 999    PRINT *,'Error in X'
  157.     ARG1 = 0.
  158.     END
  159.  
  160. Then, anyplace in the ..., GOTO 999 to do the abort, and anyplace you would
  161. invoke a function or subroutine in the ... that might want to do the abort
  162. directly, recode the function as a subroutine and pass *999 as an alternate-
  163. return argument, to be used by the called subroutine or any of the procedures
  164. it calls (i.e. apply this technique recursively :-).  See my earlier post
  165. about ENTRY for a clearer description.
  166.  
  167. Also realize that this is not a conforming program in ANSI FORTRAN 77
  168. (or in Fortran 90 for that matter):
  169.  
  170.     PROGRAM FOO
  171.     CALL BAR
  172.     END
  173.  
  174.     SUBROUTINE BAR
  175.     LOGICAL FIRST
  176.     SAVE FIRST
  177.     DATA FIRST/.TRUE./
  178. C
  179.     IF (FIRST) THEN
  180.         FIRST = .FALSE.
  181.         CALL BLETCH
  182.     END IF
  183. C
  184.     PRINT *,'BAR'
  185.     END
  186.  
  187.     SUBROUTINE BLETCH
  188.     PRINT *,'BLETCH'
  189.     CALL BAR
  190.     END
  191.  
  192. If you're lucky, the above program might print
  193.  
  194. BLETCH
  195. BAR
  196. BAR
  197.  
  198. but it is also perfectly standard-conforming for your system to delete
  199. all your files as well, since you're program is not standard-conforming
  200. due to its attempt to recurse.  :-)
  201.  
  202. Also note that even the following is not permitted by the standard:
  203.  
  204.     PRINT *,FOO(ICHAR('2'))
  205.     END
  206.  
  207.     REAL FUNCTION FOO(I)
  208.     CHARACTER*20 IFILE
  209.     IFILE = CHAR(I) // '.3'
  210.     READ (10, IFILE) FOO
  211. 10    FORMAT (F3.1)
  212.     END
  213.  
  214. (Assuming I've got the format and other such sundry details right....)
  215.  
  216. The above is not _overt_ recursion, but is nevertheless disallowed by
  217. the standard because it has _implicit_ recursion of the Fortran I/O subsystem.
  218. In particular, FOO is invoked after the start of I/O, and then FOO itself
  219. reinvokes the I/O subsystem to do the internal-file READ to translate
  220. "2.3" into a REAL value in FOO.
  221.  
  222. You might say, "well why didn't they require pre-evaluation of the arguments
  223. to the I/O statement?"  Good question.  First, I'm not sure offhand if
  224. this would be valid:
  225.  
  226.     WRITE (*,*,ERR=20) (FOO(I), FOO(I), J=1,100)
  227. 20    END
  228.  
  229.     REAL FUNCTION FOO(I)
  230.     FOO = FLOAT (I)
  231.     I = I + 1
  232.     END
  233.  
  234. If it is valid (which I think it might be, actually), it wouldn't work
  235. if preevaluation was required (I should not always end up with the value
  236. 201 or whatever it gets, if there's an error writing to *).
  237.  
  238. But, more specifically, how would this work?
  239.  
  240.     READ *, I, FOO(I)
  241.     END
  242.  
  243. Here, only after the value I is read, and while the I/O is still active,
  244. do we know what to pass to FOO for preevaluation.
  245.  
  246. Believe me, this kind of thing gives all sorts of problems to those of
  247. us who implement Fortran compiler systems, especially on weird
  248. architectures or under other such constraints!
  249.  
  250. I don't remember whether Fortran 90 lifts the restriction on recursive
  251. invocation of the I/O subsystem, offhand.  Sure would be nice, since it
  252. seems possible, and it's hard for programmers to keep themselves from
  253. violating the rule (especially given how it is so common to insert
  254. PRINT statements to debug code).
  255. --
  256.  
  257. James Craig Burley, Software Craftsperson    burley@gnu.ai.mit.edu
  258. Member of the League for Programming Freedom (LPF) lpf@uunet.uu.net
  259.