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

Abstract Interface for submodule routine definitions

Colin_W_1
Novice
2,137 Views

I have a collection of routines in submodules, many of which have an identical interface specification. The ancestor module (see attached) defines all these interfaces separately. I would like to use an ABSTRACT INTERFACE to reduce the verbosity and duplication in this module. However, I can't get the syntax right, nothing I try will compile. Anyone know how to do it?

 

0 Kudos
1 Solution
FlyingHermes
New Contributor I
2,137 Views

I'm dealing with the same problem in my codes.

Here what I'm doing:
 

module net_func_branches_interfaces_mod

   use dummy_types

   implicit none

   interface

# define _ProcedureInterface_(ProcedureName) \
      module subroutine ProcedureName(net, ib, irow, spres, epres, rate, func, derv, outlet_fprop, ehs); \
        type(t_network), target, intent(inout) :: net           ; \
        integer                , intent(in   ) :: ib            ; \
        integer                , intent(in   ) :: irow          ; \
        real(8)                , intent(in   ) :: spres         ; \
        real(8)                , intent(inout) :: epres         ; \
        real(8)                , intent(in   ) :: rate          ; \
        logical                , intent(in   ) :: func          ; \
        logical                , intent(in   ) :: derv          ; \
        type(t_flash_results)  , intent(out  ) :: outlet_fprop  ; \
        type(t_ehs)            , intent(inout) :: ehs           ; \
      end subroutine

      _ProcedureInterface_(net_func_branches_binjector)
      _ProcedureInterface_(net_func_branches_bseparator)
      _ProcedureInterface_(net_func_branches_compressor)

# undef _ProcedureInterface_

# define _ProcedureInterface_(ProcedureName) \
      module subroutine ProcedureName(net, ib, irow, spres, epres, rate, func, derv, ehs); \
        type(t_network), target, intent(inout) :: net           ; \
        integer                , intent(in   ) :: ib            ; \
        integer                , intent(in   ) :: irow          ; \
        real(8)                , intent(in   ) :: spres         ; \
        real(8)                , intent(in   ) :: epres         ; \
        real(8)                , intent(in   ) :: rate          ; \
        logical                , intent(in   ) :: func          ; \
        logical                , intent(in   ) :: derv          ; \
        type(t_ehs)            , intent(inout) :: ehs           ; \
      end subroutine

      _ProcedureInterface_(net_func_branches_fcv)
      _ProcedureInterface_(net_func_branches_nrv)
      _ProcedureInterface_(net_func_branches_pcv)

# undef _ProcedureInterface_

# define _ProcedureInterface_(ProcedureName) \
      module subroutine ProcedureName(net, ib, irow, spres, epres, rate, func, derv, fpropo, ehs); \
        type(t_network), target, intent(inout) :: net           ; \
        integer                , intent(in   ) :: ib            ; \
        integer                , intent(in   ) :: irow          ; \
        real(8)                , intent(inout) :: spres         ; \
        real(8)                , intent(inout) :: epres         ; \
        real(8)                , intent(in   ) :: rate          ; \
        logical                , intent(in   ) :: func          ; \
        logical                , intent(in   ) :: derv          ; \
        type(t_flash_results)  , intent(out  ) :: fpropo        ; \
        type(t_ehs)            , intent(inout) :: ehs           ; \
      end subroutine

      _ProcedureInterface_(net_func_branches_choke)
      _ProcedureInterface_(net_func_branches_connector)

# undef _ProcedureInterface_

   end interface

end module

 

View solution in original post

0 Kudos
14 Replies
Andrew_Smith
Valued Contributor I
2,137 Views

You could use an include file with the common arguments declared

0 Kudos
andrew_4619
Honored Contributor III
2,137 Views

I guess it depends how you want to use them. You can have an abstract interface and then define procudure pointers (of that type) to the actual implimentation routine names which then become normal rather than module subroutines in the submodule

 

I hate include files BTW

0 Kudos
Steve_Lionel
Honored Contributor III
2,137 Views

I wanted to do something similar recently, and concluded that the language does not allow for this. Ideally one would want to use PROCEDURE(abstract-name) to declare the non-abstract interface, but one can't. At least for submodules you don't have to repeat the argument list in the submodule itself using MODULE PROCEDURE (but you can if you want to.)

0 Kudos
FortranFan
Honored Contributor III
2,137 Views

@Colin W.,

To the best of my understanding, ABSTRACT INTERFACE facility in the Fortran standard does not provide any solution to what you are looking for in "reduce the verbosity and duplication.", particularly in the context of SUBMODULEs.

See this site: https://github.com/j3-fortran/fortran_proposals.

I suggest you make a proposal there with details of the facility you seek - I think it will be a useful addition to the language to include better interface facility which can reduce verbosity and duplication that then helps develop less error-prone and more expressive code.

 

0 Kudos
FlyingHermes
New Contributor I
2,138 Views

I'm dealing with the same problem in my codes.

Here what I'm doing:
 

module net_func_branches_interfaces_mod

   use dummy_types

   implicit none

   interface

# define _ProcedureInterface_(ProcedureName) \
      module subroutine ProcedureName(net, ib, irow, spres, epres, rate, func, derv, outlet_fprop, ehs); \
        type(t_network), target, intent(inout) :: net           ; \
        integer                , intent(in   ) :: ib            ; \
        integer                , intent(in   ) :: irow          ; \
        real(8)                , intent(in   ) :: spres         ; \
        real(8)                , intent(inout) :: epres         ; \
        real(8)                , intent(in   ) :: rate          ; \
        logical                , intent(in   ) :: func          ; \
        logical                , intent(in   ) :: derv          ; \
        type(t_flash_results)  , intent(out  ) :: outlet_fprop  ; \
        type(t_ehs)            , intent(inout) :: ehs           ; \
      end subroutine

      _ProcedureInterface_(net_func_branches_binjector)
      _ProcedureInterface_(net_func_branches_bseparator)
      _ProcedureInterface_(net_func_branches_compressor)

# undef _ProcedureInterface_

# define _ProcedureInterface_(ProcedureName) \
      module subroutine ProcedureName(net, ib, irow, spres, epres, rate, func, derv, ehs); \
        type(t_network), target, intent(inout) :: net           ; \
        integer                , intent(in   ) :: ib            ; \
        integer                , intent(in   ) :: irow          ; \
        real(8)                , intent(in   ) :: spres         ; \
        real(8)                , intent(in   ) :: epres         ; \
        real(8)                , intent(in   ) :: rate          ; \
        logical                , intent(in   ) :: func          ; \
        logical                , intent(in   ) :: derv          ; \
        type(t_ehs)            , intent(inout) :: ehs           ; \
      end subroutine

      _ProcedureInterface_(net_func_branches_fcv)
      _ProcedureInterface_(net_func_branches_nrv)
      _ProcedureInterface_(net_func_branches_pcv)

# undef _ProcedureInterface_

# define _ProcedureInterface_(ProcedureName) \
      module subroutine ProcedureName(net, ib, irow, spres, epres, rate, func, derv, fpropo, ehs); \
        type(t_network), target, intent(inout) :: net           ; \
        integer                , intent(in   ) :: ib            ; \
        integer                , intent(in   ) :: irow          ; \
        real(8)                , intent(inout) :: spres         ; \
        real(8)                , intent(inout) :: epres         ; \
        real(8)                , intent(in   ) :: rate          ; \
        logical                , intent(in   ) :: func          ; \
        logical                , intent(in   ) :: derv          ; \
        type(t_flash_results)  , intent(out  ) :: fpropo        ; \
        type(t_ehs)            , intent(inout) :: ehs           ; \
      end subroutine

      _ProcedureInterface_(net_func_branches_choke)
      _ProcedureInterface_(net_func_branches_connector)

# undef _ProcedureInterface_

   end interface

end module

 

0 Kudos
andrew_4619
Honored Contributor III
2,137 Views

The ability to use an abstract (generic) interface to apply a a family of submodule routines would be very helpful language feature. A common use case I see is Win API callbacks that must  all have the same (pre-defined) interface.

I suggested in #3 that you could do this with procedure pointers so I had a play to see what could be done. I managed to get something to work but maybe it is not a good way???? Discuss!

module net_branches_interfaces_mod
    implicit none
    abstract interface 
        subroutine net_branches(net, ib)
            integer                , intent(in   ) :: net
            integer                , intent(out  ) :: ib
        end subroutine net_branches
    end interface
    interface
        module subroutine   init_net_branches_interfaces_mod()
        end subroutine      init_net_branches_interfaces_mod
    end interface 
    !  use ABSTRACT INTERFACE
    procedure(net_branches), pointer :: net_branches_binjector  =>  null()
    procedure(net_branches), pointer :: net_branches_block      =>  null()
    procedure(net_branches), pointer :: net_branches_bseparator =>  null()
end module net_branches_interfaces_mod
    
submodule(net_branches_interfaces_mod) net_branches_interfaces_mod_subs
    implicit none
    contains
    
    module subroutine init_net_branches_interfaces_mod()
        net_branches_binjector  => m_net_branches_binjector
        net_branches_block      => m_net_branches_block
        net_branches_bseparator => m_net_branches_bseparator
    end subroutine init_net_branches_interfaces_mod
    
    subroutine m_net_branches_binjector(net, ib)
        integer                , intent(in   ) :: net
        integer                , intent(out  ) :: ib
        ib = net + 1
    end subroutine
    
    subroutine m_net_branches_block(net, ib)
        integer                , intent(in   ) :: net
        integer                , intent(out  ) :: ib
        ib = net + 2
    end subroutine
    
    subroutine m_net_branches_bseparator(net, ib)
        integer                , intent(in   ) :: net
        integer                , intent(out  ) :: ib
        ib = net + 3
    end subroutine
end submodule net_branches_interfaces_mod_subs

program test
    use net_branches_interfaces_mod
    implicit none
    integer :: i, j
    call init_net_branches_interfaces_mod()
    i = 1
    call net_branches_binjector(i,j)
    print *, j
end program test

 

0 Kudos
FortranFan
Honored Contributor III
2,137 Views

andrew_4619 wrote:

.. I suggested in #3 that you could do this with procedure pointers so I had a play to see what could be done. I managed to get something to work but maybe it is not a good way???? Discuss! ..

What you show with procedure pointers is a workaround that some might find interesting.  They can consider it while the Fortran language takes at least 20 years or so to offer a solution for this i.e., if at all they take this up for feature development in a future standard revision.  Meaning the earliest it can even be considered is Fortran 202Y which may come due circa 2028 and another 10 years for half-decent compiler implementations that coders can have confidence to use.

With all the teams I've worked with, this is mostly moot now because they have all moved away from modern Fortran at a very rapid clip over the last 2-3 years.  But when Fortran was on the table, the 2 big considerations were with code structures and build tool-chains:

  1. Avoid large files and encapsulate important business/technical computing instructions in separate file units to get added control as to who has access to what,
  2. Minimize build dependencies, especially with libraries and avoid compilation cascades 

SUBMODULEs provided promise to help with both of above considerations.

However if subprograms such as "m_net_branches_binjector", "m_net_branches_block", etc. are simply CONTAINed procedures in the SUBMODULE net_branches_interfaces_mod_subs, then that would have been a no-no, particularly if these procedures were large subprograms and/or they captured important and sensitive compute instructions.

But if these procedures are not big nor sensitive, then why bother with a SUBMODULE at all?  One can simply CONTAIN them in the parent MODULE (net_branches_interfaces_mod) itself.

 

0 Kudos
andrew_4619
Honored Contributor III
2,137 Views

FortranFan wrote:
 However if subprograms such as "m_net_branches_binjector", "m_net_branches_block", etc. are simply CONTAINed procedures in the SUBMODULE net_branches_interfaces_mod_subs, then that would have been a no-no, particularly if these procedures were large subprograms and/or they captured important and sensitive compute instructions.

The example code makes use of submodules so you can play with the routines without any build cascade which is important to me. I don't quite understand your point above, the routines in the submodule are exposed to the world via the abstract interface and the  procedure pointer, but other than that (to me anyway) have the same 'status' to the world as if they were module procedures exposed by that mechanism. I don't think i would in general use this construct I think would cut/paste/edit the interface N times (as I do from time to time already). Maybe you could elaborate a little so I can understand the thinking better.

0 Kudos
Colin_W_1
Novice
2,137 Views

FlyingHermes : Thanks, your suggestion is excellent. Attached is my new code.

Cheers

Qolin W

 

0 Kudos
Colin_W_1
Novice
2,137 Views

FortranFan

I have taken your advice and opened a new proposal, see:

https://github.com/j3-fortran/fortran_proposals/issues/120

0 Kudos
FortranFan
Honored Contributor III
2,137 Views

Colin W. wrote:

FortranFan

I have taken your advice and opened a new proposal, see:

https://github.com/j3-fortran/fortran_proposals/issues/120

Awesome,  thank you! 

0 Kudos
JohnNichols
Valued Contributor III
2,134 Views

What language are these programmers moving to if not Fortran?

0 Kudos
FortranFan
Honored Contributor III
2,137 Views

Nichols, John wrote:

What language are these programmers moving to if not Fortran?

@John Nichols, you're in academia: you must be aware of the languages taught by schools and universities broadly as well as in science and engineering departments globally.  Note that list is not all that different from the top 9 on IEEE survey:

https://spectrum.ieee.org/computing/software/the-top-programming-languages-2019

And these are the languages being used in industry as well.

0 Kudos
JohnNichols
Valued Contributor III
2,134 Views

Dear FortranFan:

Unluckily I am in a construction department -  I married an American women and needed a job. 

I see some code development, but a lot of what I see is Python and Matlab.   I hate MATLAB as a teaching tool, the student's I have seen do not understand how to program and the programs cannot solve large problems.   This is not true across the spectrum -- but I do not see the CompSci people. 

Our computer support people rave about Python.  Python is embedded in Rhino. I tried it to develop a FEM modeller - after 10,000 lines and by my estimate at least 20000 to go I gave up - it is not  a nice language.  It is also slow. 

I was forced into C# by the manufacturer of the accelerometer, the driver interface is written in C#.  So I spend a lot of time in that language, the real pity is put a few of the features in Fortran and you have a perfect language. 

Thank you for the IEEE reference.  

And also this forum is the best. 

John

 

 

0 Kudos
Reply