Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Inter-Module References

L__Richard_L_
Beginner
6,209 Views

I have modules that reference each other.  Is there any way to do this; e.g., pre-compiled modules?  My desired code sequence would have the following pattern:

  • module M1 source. This uses module M1, references subroutine S2, and contains array A1.  All objects are public.
  • module M2 source. This uses module M1, contains subroutine S2, and references array A1.  All objects are public.
  • main program P source.  This uses modules M1 and M2 and references subroutine S2 and array A1.

I suspect this will not compile (if I do it in one step) because of the circular references; so, I did not try it.  How about the following approach?

  • Compile M1 source as a separate step.
  • Compile M2 source as a separate step.
  • Compile P source with a command that references the pre-compiled forms of M1 and M2.

[[I apologize for earlier not submitting this as a separate thread.]]

0 Kudos
1 Solution
Paul_Curtis
Valued Contributor I
6,105 Views

Can't have circular references, and you can't get around this situation by gaming the compilation order.  Suggestion: create a module G.f90 (ie, "globals") which contains data type definitions, defined parameters, array A1, and subroutine S2.  Then: Main uses (G, M1, M2), and M1 and M2 both use G.

View solution in original post

0 Kudos
38 Replies
L__Richard_L_
Beginner
3,581 Views

Please replace M1 by M2 in the first bullet, which should read:

  • module M1 source. This uses module M2, references subroutine S2, and contains array A1.  All objects are public.
0 Kudos
Paul_Curtis
Valued Contributor I
6,106 Views

Can't have circular references, and you can't get around this situation by gaming the compilation order.  Suggestion: create a module G.f90 (ie, "globals") which contains data type definitions, defined parameters, array A1, and subroutine S2.  Then: Main uses (G, M1, M2), and M1 and M2 both use G.

0 Kudos
L__Richard_L_
Beginner
3,581 Views

Paul - Thanks - I feared that might be the case - can't game compilation order. That seems unfortunate because it would require only a second pass to patch unresolved references.  I would hope to see this capability in a future version.

0 Kudos
IanH
Honored Contributor III
3,581 Views

I would very much hope not!  Going the other way - it would be nice if the compiler yelled at me and gave me a good kick up the backside whenever I tried to create a circular module dependency.

0 Kudos
andrew_4619
Honored Contributor III
3,581 Views

IanH wrote:

I would very much hope not!  Going the other way - it would be nice if the compiler yelled at me and gave me a good kick up the backside whenever I tried to create a circular module dependency.

Agreed, you know you have circular references by the symptoms of compile failure rather than just being told how you are being dumb. I guess VS should tell you this when it tried to determine the build order. Perhaps it does if you know the correct things to set????

0 Kudos
L__Richard_L_
Beginner
3,581 Views

I used a general, easy, but unpleasant fix to my problem. I merged M1 and M2. This took care of many circular references but left me with a massive and borderline non-maintainable module. Another objection to this approach is that, for practical purposes, it is not extendable; the bigger the module the less mobile it becomes and the greater likelihood of having to add other modules to the mass. Relaxing the anti-circularity rule would avoid all this grief.

Pulling just the circular pieces into separate modules would overly complicate access to module-level objects.

0 Kudos
IanH
Honored Contributor III
3,581 Views

That's why you and many others, including myself, are keenly awaiting the implementation of submodules from F2008 in Intel's Fortran Composer Studio Parallel Deluxe PQRST with Special Sauce 201X product.

0 Kudos
Steven_L_Intel1
Employee
3,581 Views

IanH wrote:

That's why you and many others, including myself, are keenly awaiting the implementation of submodules from F2008 in Intel's Fortran Composer Studio Parallel Deluxe PQRST with Special Sauce 201X product.

Who blabbed?  But note that you have to order the EC (Extra Cheese) option to get that...

0 Kudos
L__Richard_L_
Beginner
3,581 Views

I will have to read what F2008 has to say about submodules. But, the name itself does not immediately suggest a solution to the anti-circularity issue. I hope to be pleasantly surprised.

0 Kudos
IanH
Honored Contributor III
3,581 Views

Obviously it may depend on details you've not stated.  But what I imagine you would have is...

  • module M source. This has, at least, array A1 and the interface for a separate module procedure for subroutine S2, plus perhaps other things common to former M1 and M2 and anything that needs to be collectively PUBLIC across former modules M1 and M2.
  • submodule SM1, a descendent of module M.  This contains the remaining bits of former module M1 not already in module M.
  • submodule SM2, another descendent of module M.  This contains the body of subroutine S2 and any other bits of former module M2 not already in module M.
  • main program P source.  This uses module M and references subroutine S2 and array A1

(What specifically stops you using Paul's approach in the meantime?)

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,581 Views

Richard,

>>Another objection to this approach is that, for practical purposes, it is not extendable; the bigger the module the less mobile it becomes and the greater likelihood of having to add other modules to the mass. Relaxing the anti-circularity rule would avoid all this grief.

You make things extendable by constructing hierarchies, what you are trying to do is to avoid making hierarchies.

I use Paul's approach with a globals module, plus at times I go one step further and place the contains section into a separate module. This necessitates inserting interfaces into the data portion of the module.

Globals.f90
Adata.f90
Acode.f90
Bdata.f90
Bcode.f90
...

With this construct Acode can USE Bdata, and Bcode can USE Adata with no (compiler) circular dependencies.

When Acode USEs Bdata it has the interfaces to Bcode and can call anything interfaced in Bdata to Bcode.
Same thing the other way around.

The globals.f90 would contain the items that are relatively fixed. As changing anything in globals would tend to rebuild all.

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor III
3,581 Views

Steve Lionel (Intel) wrote:

Quote:

IanH wrote:

That's why you and many others, including myself, are keenly awaiting the implementation of submodules from F2008 in Intel's Fortran Composer Studio Parallel Deluxe PQRST with Special Sauce 201X product.

 

Who blabbed?  But note that you have to order the EC (Extra Cheese) option to get that...

I'd order EC, any other chef's specials, and all the sides, hey the whole works, if I could get my hands on that "sub" !!! :-)

0 Kudos
Steven_L_Intel1
Employee
3,581 Views

Patience, grasshopper. All things in time.

0 Kudos
FortranFan
Honored Contributor III
3,581 Views

Steve Lionel (Intel) wrote:

Patience, grasshopper. All things in time.

That "patience is a virtue" is largely lost in the technology-driven world of ours - so many now keep "chomping at the bit" for the next new thing, say the next "i" device, but for a simple human like me, it's the next Fortran 2003/2008/201X feature in Intel Fortran!! :-) 

0 Kudos
L__Richard_L_
Beginner
3,581 Views

[to jimd] I appreciate your comments because they seem like a big improvement to what I am doing. I particularly like the way you laid out the suggested pattern. However, I do not understand how Acode.f90 can access data (e.g., types and arrays) in Bdata.f90. Would Acode.f90 have to call getter's and setter's in Bcode.f90 to obtain the access?

0 Kudos
andrew_4619
Honored Contributor III
3,581 Views

Richard, I'm not sure reading your original post that Jim's post exactly fully fixes your problem. What he is saying is that by separating the module data (global) declarations into separate modules (ADATA and BDATA) the Modules ACODE and BCODE can then both have USE ADATA and USE BDATA with no circularity.

I think you also have the problem that you have subroutines that are that are circular in M1 and M2 which cannot be. It may be that you need to move a few subroutines from M2 to M2 or visa versa or if this cannot work fully extract some problem subroutines into M3 where M3 will USE M1 and M2 and sit higher up the build order.

there is no magic bullet it usually needs a little but of work to sort it out.

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,581 Views

L. Richard L. wrote:

[to jimd] I appreciate your comments because they seem like a big improvement to what I am doing. I particularly like the way you laid out the suggested pattern. However, I do not understand how Acode.f90 can access data (e.g., types and arrays) in Bdata.f90. Would Acode.f90 have to call getter's and setter's in Bcode.f90 to obtain the access?

[fortran]

!  useAB.f90
program useAB
    use Interfaces
    use Adata
    use Bdata
    implicit none
    A = 0.0
    B = 0.0
    print *,A,B
    call Afoo(1.0)
    print *,A,B
    call Bfoo(2.0)
    print *,A,B
end program useAB
-------------------------

! Interfaces.f90
module Interfaces
    interface
        subroutine Afoo(x)
            use Adata
            use Bdata
            implicit none
            real :: x
        end subroutine Afoo
    end interface

    interface
        subroutine Bfoo(x)
            use Adata
            use Bdata
            implicit none
            real :: x
        end subroutine Bfoo
    end interface
end module Interfaces
---------------------------------
! Adata.f90
module Adata
    real :: A
end module Adata
-----------------------
! Acode.f90
subroutine Afoo(x)
    use Interfaces
    use Adata
    use Bdata
    implicit none
    real :: x
    A = A + x
    B = B + x
end subroutine Afoo
-------------------------
! Bdata.f90
module Bdata
    real :: B
end module Bdata
-----------------------------
! Bcode.f90
subroutine Bfoo(x)
    use Interfaces
    use Adata
    use Bdata
    implicit none
    real :: x
    call Afoo(x * 2.0)
end subroutine Bfoo
[/fortran]

Jim Dempsey

 

 

0 Kudos
IanH
Honored Contributor III
3,581 Views

(As I'm sure Jim knows - his code is not conforming because there are two interfaces for Afoo and Bfoo visible inside the Afoo and Bfoo subroutines respectively - one from being inside the actual subroutine proper and the other from the relevant interface block.  You can prevent this problem from occurring by placing an ONLY clause on the USE Interfaces statements and preventing the relevant interface block from being visible. 

Obviously woe betide anyone that manages to get a mismatch between the manually maintained interface block and the actual procedure interface. 

Sometimes this approach is unavoidable due to real circular dependencies, but - my view - you need to have a pretty good reason to go down this path.)

0 Kudos
FortranFan
Honored Contributor III
3,581 Views

Jim,

For the example you provide in Quote #18, it is unclear to me why you need to create interface blocks at all - see Ian's comments on all the potential issues with such an approach.  Instead, for your specific example, why won't you utilize host association and make use of a procedure encapsulation module as shown below?

[fortran]

!  useAB.f90

   program useAB

       use Adata, only : A

       use Bdata, only : B

       use foo, only : Afoo, Bfoo

       implicit none

       A = 0.0

       B = 0.0

       print *,A,B

       call Afoo(1.0)

       print *,A,B

       call Bfoo(2.0)

       print *,A,B

   end program useAB

   -------------------------

   ! Adata.f90

   module Adata

       implicit none

       real :: A

   end module Adata

   -----------------------

   ! Bdata.f90

   module Bdata

       implicit none

       real :: B

   end module Bdata

   -----------------------------

   ! module that encapsulates procedures

   module foo

      implicit none

      use Adata, only : A

      use Bdata, only : B

   contains

      subroutine Afoo(x)

          implicit none

          real :: x

          A = A + x       !.. HOST association would provide access to A

          B = B + x       !.. HOST association would provide access to B

      end subroutine Afoo

      -------------------------

      ! Bcode.f90

      subroutine Bfoo(x)

          implicit none

          real :: x

          call Afoo(x * 2.0)   !.. HOST association should provide an explicit

                               !   interface to Afoo

      end subroutine Bfoo

   end module foo

 

[/fortran]

 

Separately, it is unclear to me if Jim's example adequately captures the circular dependency issues faced by L. Richard L.  Otherwise, Richard needs to present a real case with actual code that fails to compile and the readers can evaluate if there is a good scheme to overcome the issue.

I strongly feel one should:

  1. make good use of host association; it also comes in handy in inlining code, especially for frequently invoked procedures e.g., utilities, math functions, etc.,
  2. steer away from INTERFACE blocks as much as possible, and
  3. always apply the ONLY attribute on USE statements

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,447 Views

FortranFan,

The point of my post was to illustrate how to separate the functions, and not how one would code the specific sample code where Bfoo calls Afoo. I left it for an exercise of the reader to write into Afoo.f90 an additional subroutine Afee, and write into Bfoo.f90 an additional subroutine Bfee that makes the calls the other way (thus completing the other half of the circle).

If one were to rely on host association, then all the code, excepting the PROGRAM, would reside in a single module. And we wanted to avoid this.

Interface modules are the only way you get access to libraries (e.g. Win32, MKL, ...), and object-only code distribution. There is nothing inherently wrong with interfaces.

Note, the source code in the library should also use the same interface module. Thus assuring interface consistency.

Jim Dempsey

0 Kudos
Reply