Search code examples
makefilefortranfortran90fortran95

Clean way to separate functions/subroutine declaration from definition in Fortran 90


I am working on a big Fortran 90 code, with a lot of modules. What bothers me is that when I modify the inner code of a function inside a module (without changing its mask), my Makefile (whose dependencies are based on "use") recompile every file that "use" that modified module, and recursively.

But when modifying the inner code of a function without touching its input/output, recompiling other files than the modified one is useless, no?

So I would like to separate the function declaration from their definition, like with the .h files in C or C++. What is the clean way to do this? Do I have to use Fortran include/preprocessor #include, or is there a "module/use" way of doing this?

I have tried something like this, but it seems to be quite nonsense...

main.f90

program prog

  use foomod_header

  integer :: i

  bar=0
  i=42
  call foosub(i)

end program prog

foomod_header.f90

module foomod_header

  integer :: bar

  interface 
    subroutine foosub(i)
      integer :: i
    end subroutine
  end interface

end module foomod_header

foomod.f90

module foomod

  use foomod_header

  contains

  subroutine foosub(i)
    integer ::i

    print *,i+bar

  end subroutine foosub

end module foomod

Solution

  • If submodules aren't an option (and they are ideal for this), then what you can do is make the procedure an external procedure and provide an interface for that procedure in a module. For example:

    ! Program.f90
    PROGRAM p
      USE Interfaces
      IMPLICIT NONE
      ...
      CALL SomeProcedure(xyz)
    END PROGRAM p
    
    ! Interfaces.f90
    MODULE Interfaces
      IMPLICIT NONE
      INTERFACE
        SUBROUTINE SomeProcedure(some_arg)
          USE SomeOtherModule
          IMPLICIT NONE
          TYPE(SomeType) :: some_arg
        END SUBROUTINE SomeProcedure
      END INTERFACE
    END MODULE Interfaces
    
    ! SomeProcedure.f90
    SUBROUTINE SomeProcedure(some_arg)
      USE SomeOtherModule
      IMPLICIT NONE
      TYPE(SomeType) :: some_arg
      ...
    END SUBROUTINE SomeProcedure
    

    Some important notes:

    • There must only ever be one interface definition for a procedure accessible in a scope. Inside a subprogram the interface for the procedure defined by the subprogram is also considered defined - hence inside the subprogram you must not permit an interface block for procedures defined by the subprogram to be accessible. In terms of the example, this means that you must not have a USE Interfaces statement without an only clause inside the SomeProcedure external procedure.

    • If you do change the arguments or similar of the procedure inside SomeProcedure.f90 you had better make sure that you change the corresponding interface block inside the module!

    • If you can use F2003, the IMPORT statement can make life easier. Otherwise you might have to have additional modules (such as SomeOtherModule in the example) to share type definitions and the like between the Interfaces module and the external procedure.

    • If you have private entities or components relevant to the procedure then Fortran's rules entity and component accessibility may prevent you using this approach.

    • Typically some sort of whole program analysis is done at high levels of optimization. That analysis is typically much slower than the actual parsing of the code - splitting out procedures in this manner may not actually shorten build times significantly under these conditions.