home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!stanford.edu!snorkelwacker.mit.edu!ai-lab!life.ai.mit.edu!burley
- From: burley@apple-gunkies.gnu.ai.mit.edu (Craig Burley)
- Newsgroups: comp.lang.fortran
- Subject: SUMMARY: CHARACTER FUNCTION as dummy argument
- Date: 15 Dec 92 04:48:25
- Organization: Free Software Foundation 545 Tech Square Cambridge, MA 02139
- Lines: 222
- Message-ID: <BURLEY.92Dec15044825@apple-gunkies.gnu.ai.mit.edu>
- NNTP-Posting-Host: apple-gunkies.gnu.ai.mit.edu
-
- Okay, I'll take a shot at summarizing what we've learned from this
- thread. I might get things wrong, so stay on your toes, you
- standards experts! :-) Also, I haven't kept track of who has contributed
- what to this discussion as I should have -- very little of it
- is original with me, and most of that is clearly stated as my
- opinion anyway. (I did come up with the problem shown in Sample 6,
- however, whereby the length of a return value is never established,
- and pointed out the inefficiency of the VAX/VMS FORTRAN extension to
- support Sample 1, for what that's worth.)
-
- First off, the specific situation being addressed is this:
-
- C Sample 1:
- SUBROUTINE X(CFUNC)
- CHARACTER*(*) CFUNC
- EXTERNAL CFUNC
- C (EXTERNAL is optional since there's a reference to CFUNC next.)
- PRINT *, CFUNC()
- END
-
- To make sure we're on the same page, what is happening here is this:
- presumably some program unit in the program calls X, passing in the
- first argument a CHARACTER FUNCTION external procedure (or, I suppose,
- an intrinsic; CHAR is the only one available in ANSI FORTRAN 77, but
- it is not standard-conforming to pass it as an actual argument
- [see 15-3 38-45]). X declares no length for the procedure via the *(*)
- construct, then references the procedure.
-
- Because X both references the CFUNC procedure and declares it as *(*),
- X is _not_ a standard-conforming program unit in Sample 1 [15-2 5-7].
-
- The following two Samples are, however, standard-conforming (and GNU
- Fortran, currently in alpha test, will accept them as soon as I change
- it to do so -- thanks folks for getting me to try this out! :-):
-
- C Sample 2
- CHARACTER*(*) CFUNC
- EXTERNAL CFUNC
- CALL Y(CFUNC)
- END
-
- C Sample 3
- SUBROUTINE Y(CFUNC)
- CHARACTER*(*) CFUNC
- EXTERNAL CFUNC
- CALL X(CFUNC)
- END
-
- The reason both of the above Samples are standard-conforming is that
- they do not contain _references_ to CFUNC, so even though CFUNC is
- clearly a procedure, [15-2 5-7] is not violated. Only a program unit
- that actually _references_ a CHARACTER FUNCTION must declare it via
- CHARACTER*n, where n is an integer constant expression.
-
- Now, it is _possible_ to extend the language to permit Sample 1, and
- VAX/VMS FORTRAN does so. However, doing so affects (negatively) the
- performance of standard-conforming code, because the return length of
- any CHARACTER FUNCTION (not just *(*) ones) must be passed along with
- the function in case the ultimate caller (the program unit doing the
- reference) declares the procedure with *(*) instead of *n. In
- my opinion, extensions that even slightly reduce the performance of
- programs that don't use them are not wise as a default. (So if I add
- the extension to GNU Fortran, which seems hard-to-impossible while it
- uses f2c-compatible calling sequences by the way, I'll add it so it has
- to be turned on via a command-line option when compiling _all_ related
- source files. Hardly seems worth it, and see below for more problems with
- this kind of extension.)
-
- As to the why's and wherefores:
-
- ANSI FORTRAN 77 is set up so dynamic allocation is _never_ required by
- a standard-conforming program, either via heap or stack allocation.
-
- When a compiler sees code like this:
-
- C Sample 4
- CHARACTER*10 CFUNC
- PRINT *, CFUNC()
- END
-
- The compiler knows it need only allocate, statically at compile time,
- 10 characters of memory to hold the value returned by CFUNC. In fact,
- generally, in a static-allocation model, it is always the _caller_ of
- a function that allocates (statically) space for the return value. The
- callee can't allocate it statically because it can't know how many
- outstanding return values must be maintained before they are no longer
- needed by the caller, as in:
-
- C Sample 5
- CHARACTER*10 CFUNC
- CALL FOO (CFUNC(), CFUNC())
- END
-
- If CFUNC was responsible for allocating space for its own return value,
- and that allocation was done statically, then the second invocation of
- CFUNC would overwrite the first (no matter the order of invocation) and
- FOO would see two identical results even though CFUNC might return
- different values each time.
-
- So, if the _caller_ has to allocate static space for the return value,
- which is indeed the case, it must know how much space to allocate.
-
- Going back to Sample 1, which is repeated here,
-
- C Sample 1:
- SUBROUTINE X(CFUNC)
- CHARACTER*(*) CFUNC
- EXTERNAL CFUNC
- C (EXTERNAL is optional since there's a reference to CFUNC next.)
- PRINT *, CFUNC()
- END
-
- the caller of CFUNC, X, does not know how much space to allocate for
- CFUNC's return value.
-
- So, how does VAX/VMS FORTRAN support code like in Sample 1? Apparently,
- it resorts to dynamic allocation in this case, but apparently it _also_
- relies on the caller of X having declared CFUNC as *n, not *(*) (which,
- as demonstrated in Samples 2 and 3, would be standard-conforming),
- and mandates that all CHARACTER FUNCTION arguments are passed along with
- info on their declared length. The latter aspect is, as I said above,
- troubling because it has to be done even for programs that don't use this
- extension, and because it seems possible (unless documented as unsupported)
- to have a reference to a CHARACTER*(*) FUNCTION that contains _no_
- information on the length of the return value. The former aspect is simply
- an extension used when necessary, but something that goes outside the
- bounds of typical ANSI FORTRAN 77 implementations.
-
- Now, one might point out that dynamic allocation could be avoided in
- Sample 1 by insisting that the caller of X (the "ancestor caller" of
- CFUNC) not only pass the return length of CFUNC but also pass a statically
- allocated place to store the return value of CFUNC. However, this would
- introduce two problems:
-
- 1. Replacing PRINT *, CFUNC() with CALL FOO (CFUNC(), CFUNC())
- would result in the same problem illustrated in Sample 5, whereby
- the place provided by the ancestor caller of CFUNC to be written
- by the first invocation of CFUNC would be overwritten by the second
- invocation before calling FOO, so FOO would see two arguments with
- the same value instead of two different values.
-
- 2. If the ancestor caller of CFUNC declared CFUNC as CHARACTER*(*) and
- EXTERNAL before passing it it to X, which as shown in Samples 2 and
- 3 would be standard-conforming, then the ancestor caller _couldn't_
- statically allocate space for X, and then if X itself was declared
- CHARACTER*(*), _no_ code in the entire program unit would declare the
- actual length of the return value of CFUNC! (This is the problem that
- VAX/VMS FORTRAN might have right now, as noted above; I think the
- only way to handle it might be to "crash" at run time when an attempt
- is made to invoke a function declared CHARACTER*(*) for which the
- length is not known by the ancestor caller.)
-
- So, once again we return to the simple requirement of a static-allocation
- strategy: the _caller_ is responsible for allocating space for the return
- value of a function, and therefore must know how much space to allocate
- at compile time. That way, CALL FOO (CFUNC(), CFUNC()) is handled
- simply by allocating space for _two_ return values, each of which is
- 10 characters long (given CHARACTER*10 CFUNC).
-
- If anyone has a system that supports the code in Sample 1 (like VAX/VMS
- FORTRAN), it'd be interesting to see how well it handled the following:
-
- C Sample 6
- CHARACTER*(*) CFUNC
- EXTERNAL CFUNC
- CALL X (CFUNC)
- PRINT *, 'Done!'
- END
- C
- SUBROUTINE X (CFUNC)
- CHARACTER*(*) CFUNC
- EXTERNAL CFUNC
- PRINT *, 'Try this:'
- PRINT *, CFUNC()
- PRINT *, 'Now try this:'
- CALL Y (CFUNC(), CFUNC())
- END
- C
- SUBROUTINE Y (ARG1, ARG2)
- CHARACTER*(*) ARG1, ARG2
- PRINT *, ARG1(1:10), LEN (ARG1)
- PRINT *, ARG2(1:10), LEN (ARG2)
- END
- C
- CHARACTER*(*) FUNCTION CFUNC ()
- INTEGER I,J
- CHARACTER C
- SAVE I,C
- DATA I/50/, C/'A'/
- DO 10 J = 1, I
- CFUNC(J:J) = C
- 10 CONTINUE
- I = 2 * I
- C = CHAR (ICHAR (C) + 1)
- END
-
- It _should_ print something like this:
-
- Try this:
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- Now try this:
- BBBBBBBBBB 100
- CCCCCCCCCC 200
- Done!
-
- Or, maybe the BB... and CC... lines would be reversed, depending on how
- the compiler orders the calls to CFUNC in the CALL Y statement.
-
- However, I can't imagine any implementation getting that "right", whatever
- right means. The interesting info is just _how_ various implementations
- fail. I claim that the only generally useful implementation is one that
- fails when compiling X by complaining that CFUNC cannot be both a CHARACTER*(*)
- FUNCTION and referenced, and that an implementation that extends the
- language so it doesn't complain so is making standard-conforming programs
- unecessarily inefficient and perhaps introducing other problems like
- the one shown above.
-
- Hmm, maybe this isn't much of a summary after all! :-)
- --
-
- James Craig Burley, Software Craftsperson burley@gnu.ai.mit.edu
- Member of the League for Programming Freedom (LPF) lpf@uunet.uu.net
-