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

dummy procedures with generic interface

Dharma
Beginner
867 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
867 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
867 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
867 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
867 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