Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.
29285 Discussions

dummy procedures with generic interface

Dharma
Beginner
1,041 Views
Hello,
I am not sure if the follwoing approach is allowed by fortran standards. I read the language reference but I could not understand the rules. Can you help me with the following code snippet. If it is not allowed can you suggest me an alternative approach. I have constructed this exmple based on the structure of a another code i am working on.

thanks
Reddy

[fortran]module genIfaceTest_m
  implicit none
  ! The idea here is that Printer needs a PrinRoutine
  ! which can take Real or complex argument.
  ! The user of the printer will be aware whether
  ! the printer will be used for printing a real or a complex
  ! and accordingly call
  ! printer%printRoutine(C) or printer%printRoutine
  ! The user must provide a prinRoutine to intiate the printer
  type Printer_t
    real :: R
    complex :: C
    procedure(genPrint), pointer, nopass :: printRoutine => null()
  contains
    procedure :: init => init_printer
  end type Printer_t

  interface genPrint
    subroutine PrintReal
      real :: R
    end subroutine PrintReal
    subroutine PrintComplex(C)
      complex :: C
    end subroutine PrintComplex
  end interface genPrint

contains
  subroutine init_printer(this,printRoutine)
    class(Printer_t), intent(inout) :: this
    procedure(genPrint) :: printRoutine
    this%printRoutine => printRoutine
  end subroutine init_printer
end module genIfaceTest_m
!-----------------------------------
!ifort -V
!Intel Fortran Intel 64 Compiler XE for applications running on Intel 64, Ver
!sion 12.1.0.233 Build 20110811
!Copyright (C) 1985-2011 Intel Corporation.  All rights reserved.
!FOR NON-COMMERCIAL USE ONLY

! Compile command
! ifort filename.F90

! Error Message::
!gendummyProc.F90(23): error #8182: The name is neither an abstract interface nor
! a procedure with an explicit interface.   [GENPRINT]
! procedure(genPrint) :: printRoutine
[/fortran]


0 Kudos
4 Replies
IanH
Honored Contributor III
1,041 Views
The interface that you nominate for a pointer or deferred binding must be for a particular procedure or abstract interface - not a generic interface as in your example.

Some alternatives are available (such as using type extension to provide specific procedures for deferred bindings in Printer_t or perhaps another separate type) - but what's best depends on what you are trying to do. Perhaps provide a little more description - "who" is providing the procedures and "who" is calling them?
0 Kudos
Dharma
Beginner
1,041 Views
Let me see if i can explain the problem. Otherwise i will try to construct a better example.
I need to use a derivied type which can handle both complex and real data variables. The underlying structure of the derived type is same except that in one instance i will compute a function which will use real data and another instance of the derived type will do the calculation using complex data. In printer code,

i will create two instances.

type(Printer_t) :: rPrinter
type(Printer_t) :: cPrinter

To intitate the printers, i will do this

call rPrinter%init(rPrinterRoutine)

call cPrinter%init(cPrinterRoutine)

where rPrinterRoutine and cPrinterRoutine are sepecific subroutine which the user provides.

the actual problem that i am working on is the following. I am constructing a derived type which will be used for assembling a matrix in the finite element method. The user must provide a subrotuine for evaluating the element matrix which can be complex data or real data. One option i can think of is to make the data complex by default and use it for real or complex element matrix. Or i create seperate bindings for complex and real subrotuines and make the init generic instead. What i am doing right now is keep the init procedure specific and let the binding be generic but as you tell this seem to be a problem.

0 Kudos
John4
Valued Contributor I
1,041 Views

The correct syntax for generic type-bound procedures is:

[fortran]    type :: t1
        real:: r
        complex:: c
    contains
        procedure :: init_r
        procedure :: init_c
        generic :: init => init_r, init_c
    end type
[/fortran]
Where the procedures init_r and init_c already exist. If you want the user to strictly provide the init_r and init_c procedures, then:
[fortran]module mod1
    type, abstract :: t1
        real :: r
        complex :: c
    contains
        procedure(i_real_proc), deferred :: init_r
        procedure(i_complex_proc), deferred :: init_c
        generic :: init => init_r, init_c
    end type

    abstract interface
        subroutine i_real_proc(this, r)
            import
            class(t1), intent(INOUT) :: this
            real, intent(IN) :: r
        end subroutine

        subroutine i_complex_proc(this, r)
            import
            class(t1), intent(INOUT) :: this
            complex, intent(IN) :: r
        end subroutine
    end interface
end module
[/fortran]
Since both subroutines have been deferred, this option requires the user to provide them when extending the abstract type, even if he only intends to use one of them ---you could provide some dummy subroutines (e.g., dummy_init_r and dummy_init_c) that do nothing but match the abstract interfaces, so that the user has an easy way to provide the one he intends not to use:
[fortran]module usermod

   use mod1

   type, extends(t1) :: usert
   contains
      procedure :: init_r => user_init_r
      procedure :: init_c => dummy_init_c
   end type
contains
   subroutine user_init_r(this, r)
   ...
end usermod
[/fortran]
You could even declare t1 non-abstract from the beginning and just provide the two dummy ones, but then the user might not bother to override them and then complain that your code doesn't work :) .

One interesting option is to work with the data types you want and let the user handle the details he wants, for example:

[fortran]module mod2
    use iso_fortran_env

    implicit none
    private

    type, abstract, public :: t1
        class(*), allocatable :: d(:)
    contains
        procedure(i_printer_proc), deferred :: printer
        procedure :: mycode
    end type

    abstract interface
        subroutine i_printer_proc(this)
            import
            class(t1), intent(IN) :: this
        end subroutine
    end interface
contains
    subroutine mycode(this, input)
        class(t1), intent(INOUT) :: this
        class(*), intent(IN) :: input
        class(*), allocatable :: copy(:)

        !check if type is supported
        select type (input)
            type is (real)
            type is (complex)
            class default
                write (ERROR_UNIT, '(/ A)') 'Unsupported type'
                return
        end select

        allocate (this%d(9), SOURCE = input)

        !do something
        select type (e => this%d)
            type is (real)
                e = e ** 2
            type is (complex)
                e = CONJG(e)
        end select

        !print
        call this%printer()
    end subroutine

end module mod2

module mod3

    use mod2
    implicit none

    type, extends(t1), public :: usert
    contains
        procedure :: printer => user_printer
    end type

contains
    subroutine user_printer(this)
        class(usert), intent(IN) :: this
        real, allocatable :: r(:)
        complex, allocatable :: c(:)

        select type(e => this%d)
            type is (real)
                allocate (r(SIZE(e)))
                r = e
                write (*, '(/ "real:" / (3F4.1))') r
            type is (complex)
                allocate (c(SIZE(e)))
                c = e
                write (*, '(/ "complex:" /  (3(:"(", F4.1, ",", F4.1, ")")))') c
        end select
    end subroutine
end module mod3

use mod3

implicit none

type(usert) :: a
complex :: aux = CMPLX(3., -2)

call a%mycode(1)
call a%mycode(2.)
call a%mycode(aux)

end[/fortran]

In this case, you provide mod2, which must handle any of the supported data types, and ignore the unsupported ones. The user can do something similar ---i.e., it's up to the user to have a printer for a certain data type.

0 Kudos
Dharma
Beginner
1,041 Views
Thanks John for the interesting alternatives. When i started coding for the project, intel did not support source= allocation for unlimited polymorphic objects so i had to keep the data types specific. But now intel compiler does suppport this featrue. I will try to mkae use of your suggestions.

Reddy
0 Kudos
Reply