MODULE and EXTERNAL query Goto page 1, 2  Next
Author Message
Kenneth_Smith Joined: 18 May 2012
Posts: 197
Location: Hamilton, Scotland. Posted: Tue Jul 22, 2014 8:11 pm    Post subject: MODULE and EXTERNAL query I've recently been trying to get my head around MODULES and have even stopped using COMMON blocks. However I'm puzzled by some of the concepts associated with MODULES.

Here we have a function, subroutine and main routine, written in F77 as I was taught 30+ years ago. This does what I expect it to do.

 Code: FUNCTION F(X) IMPLICIT NONE DOUBLE PRECISION F, X  F = SIN(X) RETURN END SUBROUTINE SIMPSON(F,A,B,INTEGRAL,N) IMPLICIT NONE DOUBLE PRECISION F, A, B, INTEGRAL,S DOUBLE PRECISION H, X INTEGER NINT INTEGER N, I IF((N/2)*2.NE.N) N=N+1 S = 0.0 H = (B-A)/DFLOAT(N) DO I=2, N-2, 2    X   = A+DFLOAT(I)*H    S = S + 2.0*F(X) + 4.0*F(X+H) END DO INTEGRAL = (S + F(A) + F(B) + 4.0*F(A+H))*H/3.0 RETURN END SUBROUTINE SIMPSON PROGRAM MAIN IMPLICIT NONE DOUBLE PRECISION F, A, B, INTEGRAL INTEGER N EXTERNAL F A = 0.0 B = 3.1415926 N = 64 WRITE(*,100) CALL SIMPSON(F,A,B,INTEGRAL,N) WRITE (*,101) N, INTEGRAL 100   format('     nint   Simpson') 101   format(i9,1pe15.6) end

Now moving the function and subroutine to a module we get:

 Code: MODULE M1 IMPLICIT NONE CONTAINS FUNCTION F(X) DOUBLE PRECISION F, X  F = SIN(X) RETURN END FUNCTION F SUBROUTINE SIMPSON(F,A,B,INTEGRAL,N) DOUBLE PRECISION F, A, B, INTEGRAL,S DOUBLE PRECISION H, X INTEGER NINT INTEGER N, I IF((N/2)*2.NE.N) N=N+1 S = 0.0 H = (B-A)/DFLOAT(N) DO I=2, N-2, 2    X   = A+DFLOAT(I)*H    S = S + 2.0*F(X) + 4.0*F(X+H) END DO INTEGRAL = (S + F(A) + F(B) + 4.0*F(A+H))*H/3.0 RETURN END SUBROUTINE SIMPSON END MODULE M1 PROGRAM MAIN USE M1 IMPLICIT NONE DOUBLE PRECISION  A, B, INTEGRAL INTEGER N EXTERNAL F A = 0.0 B = 3.1415926 N = 64 WRITE(*,100) CALL SIMPSON(F,A,B,INTEGRAL,N) WRITE (*,101) N, INTEGRAL 100   FORMAT('     NINT   SIMPSON') 101   FORMAT(I9,1PE15.6) END

This also works, but looking at the main program I think the function F is defined in the module M1, so because of the USE M1 statement the EXTERNAL F statement is not required. But when I delete this, I get an error. Can somebody give me a (simple) explanation of why the EXTERNAL statement is still required?

Thanks
Ken   JohnCampbell

Joined: 16 Feb 2006
Posts: 2079
Location: Sydney Posted: Wed Jul 23, 2014 2:32 am    Post subject: Ken,

I am not an expert in the use of CONTAINS or EXTERNAL but I would be inclined to use EXTERNAL, even though it is not always required.
It appears that if you do not use EXTERNAL in the program, then F becomes a local real*8 variable. (what about implicit none ??) This might say something about the use of contained functions. It also indicates that in some circumstances there can be both local and contained variables of the same name, which is confusing and bad coding if allowed.
My recommendation is to use EXTERNAL, even if the compiler sometimes guesses it is the case, as in SUBROUTINE SIMPSON.
Another point is that although the function F is contained in M1, this has no linkage to the external function F referenced in SIMPSON. Modules don't override the argument list.

It is interesting, that although FTN95 identifies F in Subroutine simpson as implied external, it does not for the contained function F referenced in the Program. I do not know if this is standard complying or not.
Again, the use of EXTERNAL statements does provide clearer documentation of what you intended.
I changed to the following code to test what was happening.
 Code: MODULE M1  IMPLICIT NONE  CONTAINS  FUNCTION F(X)  DOUBLE PRECISION F, X   F = SIN(X)  RETURN  END FUNCTION F  FUNCTION C(X)  DOUBLE PRECISION C, X   C = 1.0  RETURN  END FUNCTION C  FUNCTION U(X)  DOUBLE PRECISION U, X   U = X  RETURN  END FUNCTION U  SUBROUTINE SIMPSON (F,A,B,INTEGRAL,N)  DOUBLE PRECISION F, A, B, INTEGRAL  INTEGER N  EXTERNAL F !  DOUBLE PRECISION H, X,S  INTEGER I  IF ((N/2)*2.NE.N) N=N+1  S = 0.0  H = (B-A)/DFLOAT(N)  DO I=2, N-2, 2     X = A+DFLOAT(I)*H     S = S + 2.0*F(X) + 4.0*F(X+H)  END DO  INTEGRAL = (S + F(A) + F(B) + 4.0*F(A+H))*H/3.0  RETURN  END SUBROUTINE SIMPSON  END MODULE M1  PROGRAM MAIN  USE M1  IMPLICIT NONE  DOUBLE PRECISION  A, B, INTEGRAL  INTEGER N, i  EXTERNAL F,C,U  A = 0.0  B = 4 * atan (1.0d0) ! 3.1415926  do i = 3,6    N = 2**i      WRITE(*,*) '     NINT   SINE SIMPSON'    CALL SIMPSON (F,A,B,INTEGRAL,N)    WRITE (*,101) N, INTEGRAL, (INTEGRAL-2)      WRITE(*,*) '     NINT   CONST SIMPSON'    CALL SIMPSON (C,A,B,INTEGRAL,N)    WRITE (*,101) N, INTEGRAL, (INTEGRAL-B)      WRITE(*,*) '     NINT   LINEAR SIMPSON'    CALL SIMPSON (U,A,B,INTEGRAL,N)    WRITE (*,101) N, INTEGRAL, (INTEGRAL - b**2/2)  end do  101   FORMAT(I9,' Int',ES15.6,' err',ES15.6)  END   LitusSaxonicum

Joined: 23 Aug 2005
Posts: 1972
Location: Yateley, Hants, UK Posted: Wed Jul 23, 2014 4:54 pm    Post subject: For me the trick is to ask a question along the lines of 'How does such-and-such a routine know what type variable is?' which is an anthropomorphised version of the somewhat longer question 'How does the compiler know ...' In the case of F in the list of parameters to SUBROUTINE SIMPSON, SIMPSON cannot know that F is a FUNCTION unless it is declared EXTERNAL within SIMPSON (Old-style Fortran), but SIMPSON can guess that F is a FUNCTION when calculating INTEGRAL because of the way it is invoked. Indeed, F might be a FUNCTION here or simply an undeclared array - no-one can tell until you LINK it and see if there is a FUNCTION F. FTN95 is pretty clever, but it has two choices and one of them must be wrong, mustn't it? I suppose as A is REAL, then you could guess that F is an external function, but it is easier and completely unambiguous if you declare it to be so. I don't use he later facilities in Fortran, but perhaps if F(X) was defined in one module, and that module was USEd inside SUBROUTINE SIMPSON, then that might do the trick, as the compiler could look inside the MODULE for the definition of F - but my guess is that it is also done at LINK time. Eddie   simon

Joined: 05 Jul 2006
Posts: 158 Posted: Wed Jul 23, 2014 5:51 pm    Post subject: John is right: the problem is in SUBROUTINE simpson, where you should declare F as EXTERNAL. But you do not need to declare it as EXTERNAL in the main program because of the USE statement.   Kenneth_Smith Joined: 18 May 2012
Posts: 197
Location: Hamilton, Scotland. Posted: Wed Jul 23, 2014 7:48 pm    Post subject: Thanks for your suggestions/insights on this - very much appreciated.

Looking at this again, I've now found that the following version of the code, without any EXTERNAL references, fails with Checkmate Win 32 but runs fine with Release Win 32. Again not exactly as expected. I will stick to using EXTERNAL references from now on.

Cheers

Ken

 Code: MODULE M1       IMPLICIT NONE       CONTAINS       FUNCTION F(X)       DOUBLE PRECISION F, X       F = SIN(X)       RETURN       END FUNCTION F       SUBROUTINE SIMPSON(F,A,B,INTEGRAL,N)       DOUBLE PRECISION F, A, B, INTEGRAL,S       DOUBLE PRECISION H, X       INTEGER NINT       INTEGER N, I       IF((N/2)*2.NE.N) N=N+1       S = 0.0       H = (B-A)/DFLOAT(N)       DO I=2, N-2, 2         X   = A+DFLOAT(I)*H         S = S + 2.0*F(X) + 4.0*F(X+H)       END DO       INTEGRAL = (S + F(A) + F(B) + 4.0*F(A+H))*H/3.0       RETURN       END SUBROUTINE SIMPSON       END MODULE M1       PROGRAM MAIN       USE M1       IMPLICIT NONE       DOUBLE PRECISION  A, B, INTEGRAL       INTEGER N        A = 0.0       B = 3.1415926       N = 64       WRITE(*,100)       CALL SIMPSON(F,A,B,INTEGRAL,N)       WRITE (*,101) N, INTEGRAL  100  FORMAT('     NINT   SIMPSON')  101  FORMAT(I9,1PE15.6)       END   mecej4

Joined: 31 Oct 2006
Posts: 1093 Posted: Wed Jul 23, 2014 10:12 pm    Post subject: For situations such as this, what you need is the ability to declare an Abstract Interface to a subprogram, so that any subroutine/function with that interface can be used as an actual argument to a subroutine that applies an algorithm (such as quadrature) to the specific argument function passed to it as an argument. Fortran 2003 and above allow abstract interfaces, and here is a version of your program that uses this feature. FTN95 does not allow this feature, but other Fortran compilers such as Gfortran do. Note the complete absence of variables with the attribute EXTERNAL.
 Code: MODULE FNI    ABSTRACT INTERFACE       DOUBLE PRECISION FUNCTION FUN(X)       implicit NONE       double precision, intent(in) :: x       end function    END INTERFACE END MODULE FNI MODULE SIMP CONTAINS SUBROUTINE SIMPSON(F,A,B,INTEGRAL,N) USE FNI DOUBLE PRECISION, INTENT(IN) :: A, B DOUBLE PRECISION, INTENT(OUT) :: INTEGRAL DOUBLE PRECISION S,H,X PROCEDURE (FUN) :: F INTEGER N, I IF((N/2)*2.NE.N) N=N+1 S = 0.0 H = (B-A)/N DO I=2, N-2, 2    X   = A+I*H    S = S + 2.0*F(X) + 4.0*F(X+H) END DO INTEGRAL = (S + F(A) + F(B) + 4.0*F(A+H))*H/3.0 RETURN END SUBROUTINE SIMPSON END MODULE SIMP PROGRAM MAIN USE FNI USE SIMP IMPLICIT NONE DOUBLE PRECISION  A, B, INTEGRAL INTEGER N PROCEDURE(FUN), POINTER :: FPTR A = 0d0 B = 4*atan(1d0) N = 64 FPTR=>F WRITE(*,100) CALL SIMPSON(FPTR,A,B,INTEGRAL,N) WRITE (*,101) N, INTEGRAL 100   FORMAT('     NINT   SIMPSON') 101   FORMAT(I9,1PE15.6) CONTAINS   DOUBLE PRECISION FUNCTION F(X)      DOUBLE PRECISION, INTENT(IN) :: X      F=SIN(X)      return   END FUNCTION F END PROGRAM   JohnCampbell

Joined: 16 Feb 2006
Posts: 2079
Location: Sydney Posted: Thu Jul 24, 2014 3:15 am    Post subject: Mecej4, Thanks very much for the example of a F2003 approach, as I would not have known this could be done without your post. Unfortunately this more scares me, as I find this general change to verbosity and complexity in Fortran to be a sign of extinction. How many new programmers could code that ? Isn't using EXTERNAL a better solution. I looked more at the problem of what FTN95 is doing, as I think there is a bug for the/check option to fail. I placed an EXTERNAL F statement in Subroutine SIMPSON, but no EXTERNAL in Program main. I compiled with /check /lgo and this initiated SDBG. I then hovered the mouse over the arguments of SIMPSON, which identified that variables A, B INTEGRAL and N were scalar variables, BUT F had no debug information. Clearly FTN95 identifiers F as something different but why didn't it associate F with the included function for the /check test ? What else could it be as IMPLICIT NONE has been used (twice?) FTN95 did associate the included function F for the release mode. The error message appears ambiguous as SDBG does identify F to be a procedure in the Program Main, if not a procedure then at least not a variable. For the alternative of EXTERNAL in Program main and no EXTERNAL in Subroutine Simpson, /check finds no problem, indicating that FTN95 can guess the status of F in the subroutine. The simple solution is to place an EXTERNAL statement in Program main, (which I think is required even though a contained function of the same name is available ? EXTERNAL would definitely be required if function F was not included). I would also place an EXTERNAL statement in Subroutine Simpson, although I am not sure that this is required. ( I must admit that by the end of this post I am less certain of what the standard requires. ) John   mecej4

Joined: 31 Oct 2006
Posts: 1093 Posted: Thu Jul 24, 2014 5:25 am    Post subject: John, the F2003 approach using Abstract interfaces really shows its worth when the type of a function used as an actual argument, or the argument list of the function itself, is incorrect. This type of error in Fortran 9X code cannot be detected at compile time, although compilers such as FTN95 and Fujitsu/Lahey are able to emit extra code to catch such errors at run time. Try the Simpson example again, but this time deliberately declare the type or the argument of F(X) REAL instead of DOUBLE PRECISION. A Fortran 2003 compiler will flag the error at compile time. In our example, F is a simple function of a single variable. When F is a user-defined type, or its argument list is long and complicated, using an abstract interface will save you a lot of grief. EXTERNAL cannot do this, as the attribute simply tells the compiler "Don't worry about this symbol, let the linker take care of it!" Furthermore, EXTERNAL only declares the procedure name to be global in scope, and is completely ignorant of the number, type and sequence of the list of arguments to that procedure. EXTERNAL is not needed for procedures whose declarations are within the active scope, such as module procedures or contained procedures. I am sure you have spent more time debugging mismatched subroutine argument lists than you planned to, as have most experienced programmers. Abstract interfaces can save us a lot of time, if we learn about this feature. It does take time to learn to code abstract interfaces, but the ability to catch errors at compile time rather than during run time makes the effort worthwhile. Those who use C may remember the progression from old "K&R C" to ANSI C in the 1980s, with function prototype declarations enabling checking for correct function calls at compile time. Abstract interfaces provide more or less the same facility for Fortran, but came a couple of decades later.   JohnCampbell

Joined: 16 Feb 2006
Posts: 2079
Location: Sydney Posted: Thu Jul 24, 2014 6:11 am    Post subject: mecej4,

Thanks for your explanation. For many years my solution to the argument list problem was to use included COMMON then used MODULE, although recently I am returning to libraries of routines with argument lists for data structure flexibility. I always wanted the linker to be able to do an argument count check for routines, although optional arguments can change that. Unfortunately, the linker has never been part of the Fortran Standard.

You said:
 Quote: EXTERNAL is not needed for procedures whose declarations are within the active scope, such as module procedures or contained procedures.

Doesn't this describe the case we have in this post ? This should imply that EXTERNAL is not required for function F in Program main, as the function is contained in the used module.

John   mecej4

Joined: 31 Oct 2006
Posts: 1093 Posted: Thu Jul 24, 2014 2:51 pm    Post subject: Re: JohnCampbell wrote: I always wanted the linker to be able to do an argument count check for routines, although optional arguments can change that.

The STDCALL linkage convention used by Microsoft Fortran, CVF/DVF and others allowed limited checking on the number of arguments. It could not, however, detect errors such as "call sub(m,n,X)" being written when "call sub(X,m,n)" should have been used. If, within "sub" one had "DIMENSION X(m,n)", you can imagine what can happen at run time.

As you noted, STDCALL could not handle optional arguments, and its usage gave false confidence that subroutine calls had correct argument lists if the linker did not give error messages such as "XYZ@12 not found".   davidb

Joined: 17 Jul 2009
Posts: 554
Location: UK Posted: Thu Jul 24, 2014 6:05 pm    Post subject: As well as putting "EXTERNAL F" in subroutine SIMPSON you can add an interface block. This is the "pure" Fortran 95 way of doing things.

This has the virtue that the arguments of F (type and intent) will be checked by the compiler.

If "EXTERNAL F" is used the compiler has no way of knowing whether the arguments expected of F match the calls made in SIMPSON or not.

Note that you can also put "DOUBLE PRECISION, EXTERNAL :: F" instead of using a separate "EXTERNAL F".

Here is the interface block approach (note you need to remove EXTERNAL F from the main program because it is an error).

BTW B = 3.1415926 in the main program should be B = 3.1415926d0 to get the double precision representation of the literal constant 3.1415926.

 Code: MODULE M1 IMPLICIT NONE CONTAINS FUNCTION F(X) DOUBLE PRECISION F, X  F = SIN(X) RETURN END FUNCTION F SUBROUTINE SIMPSON(F,A,B,INTEGRAL,N) DOUBLE PRECISION F, A, B, INTEGRAL,S DOUBLE PRECISION H, X INTEGER NINT INTEGER N, I INTERFACE    FUNCTION F(X)    DOUBLE PRECISION F, X    END FUNCTION F END INTERFACE IF((N/2)*2.NE.N) N=N+1 S = 0.0 H = (B-A)/DFLOAT(N) DO I=2, N-2, 2    X   = A+DFLOAT(I)*H    S = S + 2.0*F(X) + 4.0*F(X+H) END DO INTEGRAL = (S + F(A) + F(B) + 4.0*F(A+H))*H/3.0 RETURN END SUBROUTINE SIMPSON END MODULE M1 PROGRAM MAIN USE M1 IMPLICIT NONE DOUBLE PRECISION  A, B, INTEGRAL INTEGER N A = 0.0 B = 3.1415926 N = 64 WRITE(*,100) CALL SIMPSON(F,A,B,INTEGRAL,N) WRITE (*,101) N, INTEGRAL 100   FORMAT('     NINT   SIMPSON') 101   FORMAT(I9,1PE15.6) END

_________________
Programmer in: Fortran 77/95/2003/2008, C, C++ (& OpenMP), java, Python, Perl   LitusSaxonicum

Joined: 23 Aug 2005
Posts: 1972
Location: Yateley, Hants, UK Posted: Thu Jul 24, 2014 8:27 pm    Post subject: DavidB: don't you always need the D0 to make constants DOUBLE PRECISION? (FTN95 has a default real type option to do it). I always define PI with 4.0D0*DATAN(1.0D0) in an assignment statement but then I know what that means. Interface blocks (whether Fortran 2003 [mecej4] abstract ones or plain old Fortran 95 [DavidB] ones) look to me to be a wonderful solution to a problem that I don't have but I can see that other people are likely to have, especially large teams of mixed ability programmers working on the same problem or code. I don't have it because I make strenuous efforts to avoid passing function names as subprogram parameters - although Clearwin+ forces you to do it and is a reason why I didn't use it for years. In fact I never used EXTERNAL until I used Clearwin+ and don't use it for anything else. Indeed, the original poster didn't need to pass the subprogram name as a parameter, in which case, the compiler would have assumed that F was a function. He only needed to pass the function name if SIMPSON needed to run with other functions, and they could be selected by passing an integer or other code from a predefined list. Les Hatton and others used to recommend giving named BLOCK DATA routines an EXTERNAL reference somewhere in the program to make sure the linker picks it up, but BLOCK DATA is eschewed now, and using multiple named BLOCK DATA routines is one of those things I always thought was plain weird. Eddie   mecej4

Joined: 31 Oct 2006
Posts: 1093 Posted: Thu Jul 24, 2014 8:58 pm    Post subject: Re: LitusSaxonicum wrote: I make strenuous efforts to avoid passing function names as subprogram parameters.... Indeed, the original poster didn't need to pass the subprogram name as a parameter, in which case, the compiler would have assumed that F was a function. He only needed to pass the function name if SIMPSON needed to run with other functions, and they could be selected by passing an integer or other code from a predefined list. Eddie
Would you pay for a compiler that restricted you to compiling one selected source code from a repertoire of, say 100, based on an integer between 1 and 100 that you are allowed to choose? Would you pay for a library such as NAG or IMSl if they supplied a library routine that you wanted to use for quadrature, if they restricted you to using one of fifty integrands?

When writing a routine that implements a standard algorithm, we want the implementation to be usable on any problem of the user's choosing as long as that problem fits within the specifications of the problem class. Because these library routines are written and maintained by experts, the typical user should not be required/allowed to use the source code of the library. In such situations, one needs a clean separation between algorithm code and user function code. Subroutine arguments of procedure type allow this separation to be made. We gain breadth in applicability, but there is a small price to pay to make the linkage work correctly.   davidb

Joined: 17 Jul 2009
Posts: 554
Location: UK Posted: Thu Jul 24, 2014 11:01 pm    Post subject: Re: LitusSaxonicum wrote: DavidB: don't you always need the D0 to make constants DOUBLE PRECISION? (FTN95 has a default real type option to do it). I always define PI with 4.0D0*DATAN(1.0D0) in an assignment statement but then I know what that means. Eddie

Absolutely you do! In this case the other constants are all whole numbers 0.0, 2.0, 3.0, 4.0 so there is no loss of precision converting the REAL values to DOUBLE PRECISION. But as a general rule I would always write 4.0d0 etc if I am programming double precision expressions. You don't need DATAN(1.0D0) of course, you can just used ATAN(1.0D0).

 LitusSaxonicum wrote: Interface blocks (whether Fortran 2003 [mecej4] abstract ones or plain old Fortran 95 [DavidB] ones) look to me to be a wonderful solution to a problem that I don't have but I can see that other people are likely to have, especially large teams of mixed ability programmers working on the same problem or code.

Sometimes I am writing the code that calls the subroutine or function provided as an argument and "someone else" is writing the function and the call to my code. By putting my code in a module and using an interface block to define what the function must look like, I can ensure the other person will get a compiler error if they make a mistake (rather than have them pester me about their call not working). Its a form of programming by contract. I make the contract and they have to follow it.

Like you I rarely make a mistake using EXTERNAL when I am programming all parts of the problem, but the extra protection afforded by the interface does no harm and I find provides valuable code documentation when reviewing things months or years on.
_________________
Programmer in: Fortran 77/95/2003/2008, C, C++ (& OpenMP), java, Python, Perl   davidb

Joined: 17 Jul 2009
Posts: 554
Location: UK Posted: Thu Jul 24, 2014 11:06 pm    Post subject: Re: mecej4 wrote: When writing a routine that implements a standard algorithm, we want the implementation to be usable on any problem of the user's choosing as long as that problem fits within the specifications of the problem class. Because these library routines are written and maintained by experts, the typical user should not be required/allowed to use the source code of the library. In such situations, one needs a clean separation between algorithm code and user function code. Subroutine arguments of procedure type allow this separation to be made. We gain breadth in applicability, but there is a small price to pay to make the linkage work correctly.

Very true. And having a mechanism to control/check the consistency of arguments passed to the subroutine, including a definition of what the "user function" must look like, is an important part of this.
_________________
Programmer in: Fortran 77/95/2003/2008, C, C++ (& OpenMP), java, Python, Perl   Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First
 All times are GMT + 1 HourGoto page 1, 2  Next Page 1 of 2