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

Passing function as an argument

da-james
Novice
3,243 Views

I have been trying to pass a function into my rk4 subroutine with no progress. The code I have is:

mod_math.f90:

module mod_math

implicit none

! I initially tried to have my interface more general for different system sizes

! but got frustrated so fixed it for a 2d system since that's my use case atm

interface
function f2d(t, y, d) &
result(dy)
real(8), intent(in) :: t
real(8), intent(in) :: d(:,:)
real(8) :: y(2)
real(8) :: dy(2)
end function f2d
end interface

contains

subroutine rk4(df, y, ti, tf, n, data)
! arguments
procedure(f2d), pointer :: df
real(8), pointer, intent(inout) :: y(:)
real(8), intent(in) :: ti, tf
integer(4), intent(in) :: n
real(8), pointer, intent(in) :: data(:,:)

! ...

end subroutine

end module mod_math

mod_func.f90:

module mod_func

implicit none

contains

function func(t, u, d) &
result(du)
! calling variables
real(8), intent(in) :: t
real(8), intent(in) :: u(2)
real(8), pointer, intent(in) :: d(:,:)
real(8) :: du(2)

! ...

end function func

end module mod_func

main.f90:

program main

use mod_math, only : rk4, f2d
use mod_func, only : func

implicit none

procedure(f2d), pointer :: df => null()

! this causes the error
df => func()

! I've omitted the other variables, because it seems the error

! happens on the line above before I even go to the rk4 part.

! rk4(df, y0, ti, tf, n, data)

end program main

The error I get is: error #8178: The procedure pointer and the procedure target must have matching arguments.

My old rk4 was from the "Numerical Recipes", but I believe that since it's F77 it seems to get errors since it passes functions by using

external :: func

Now the newer 2018 standard seems to use procedure pointers. Since I've been trying to learn the newer standards I've been re-writing some of my code and can't seem to get the procedure to point correctly. I think another difference is that before I ran my old rk4 on the gfortran compiler which seemed to be more lax about interfaces and using the ifort is also newer for me. 

I have looked at several resources online, and I'm not sure how much more I can fix my code.

Any help would be appreciated!

Labels (1)
0 Kudos
1 Solution
FortranFan
Honored Contributor II
3,228 Views
@da-james wrote:

.. Any help would be appreciated!

 

@da-james ,

You nearly had it.  The procedure pointer assignment instruction does not need parenthesis on the target.   You also had a mismatch in your interface relative to your function implementation in terms of missing POINTER attribute on dummy argument 'd' and the INTENT with argument 'y' - I presume you wanted them in the interface.

Taking a bit of an editorial liberty with the use of abstract interface to signal the intent of declaring a signature of an expected argument and with the use of a helpful "I" prefix with such signatures and of course with defined kinds, here's revised code you can try.

module kinds_m

   ! Choose suitable kind for integer objects with their expected range
   integer, parameter :: I4 = selected_int_kind( r=9 )   !<-- Expected is a '4-byte' integer

   ! Choose minimum desired precision for floating-point objects
   integer, parameter :: R8 = selected_real_kind( p=12 ) !<-- Expected is a '8-byte' real

end module

module mod_math

   use kinds_m, only : I4, R8

   implicit none

   abstract interface
      function Ifunc_2d(t, y, d) &
            result(dy)
         import :: I4, R8
         implicit none
         real(R8), intent(in) :: t
         real(R8), intent(in) :: y(2)
         real(R8), intent(in), pointer :: d(:,:)
         real(R8) :: dy(2)
      end function Ifunc_2d
   end interface

contains

   subroutine rk4(df, y, ti, tf, n, data)
      ! arguments
      procedure(Ifunc_2d), pointer :: df
      real(R8), pointer, intent(inout) :: y(:)
      real(R8), intent(in) :: ti, tf
      integer(I4), intent(in) :: n
      real(R8), pointer, intent(in) :: data(:,:)

      ! ...

   end subroutine

end module mod_math
module mod_func

   use kinds_m, only : I4, R8

   implicit none

contains

   function func(t, u, d) &
         result(du)
      ! calling variables
      real(R8), intent(in) :: t
      real(R8), intent(in) :: u(2)
      real(R8), pointer, intent(in) :: d(:,:)
      real(R8) :: du(2)

      ! ...

   end function func

end module mod_func

program main

   use mod_math, only : rk4, Ifunc_2d
   use mod_func, only : func

   implicit none

   procedure(Ifunc_2d), pointer :: df => null()

   ! this causes the error
   df => func

   ! I've omitted the other variables, because it seems the error

   ! happens on the line above before I even go to the rk4 part.

   ! rk4(df, y0, ti, tf, n, data)

end program main

 

View solution in original post

8 Replies
FortranFan
Honored Contributor II
3,229 Views
@da-james wrote:

.. Any help would be appreciated!

 

@da-james ,

You nearly had it.  The procedure pointer assignment instruction does not need parenthesis on the target.   You also had a mismatch in your interface relative to your function implementation in terms of missing POINTER attribute on dummy argument 'd' and the INTENT with argument 'y' - I presume you wanted them in the interface.

Taking a bit of an editorial liberty with the use of abstract interface to signal the intent of declaring a signature of an expected argument and with the use of a helpful "I" prefix with such signatures and of course with defined kinds, here's revised code you can try.

module kinds_m

   ! Choose suitable kind for integer objects with their expected range
   integer, parameter :: I4 = selected_int_kind( r=9 )   !<-- Expected is a '4-byte' integer

   ! Choose minimum desired precision for floating-point objects
   integer, parameter :: R8 = selected_real_kind( p=12 ) !<-- Expected is a '8-byte' real

end module

module mod_math

   use kinds_m, only : I4, R8

   implicit none

   abstract interface
      function Ifunc_2d(t, y, d) &
            result(dy)
         import :: I4, R8
         implicit none
         real(R8), intent(in) :: t
         real(R8), intent(in) :: y(2)
         real(R8), intent(in), pointer :: d(:,:)
         real(R8) :: dy(2)
      end function Ifunc_2d
   end interface

contains

   subroutine rk4(df, y, ti, tf, n, data)
      ! arguments
      procedure(Ifunc_2d), pointer :: df
      real(R8), pointer, intent(inout) :: y(:)
      real(R8), intent(in) :: ti, tf
      integer(I4), intent(in) :: n
      real(R8), pointer, intent(in) :: data(:,:)

      ! ...

   end subroutine

end module mod_math
module mod_func

   use kinds_m, only : I4, R8

   implicit none

contains

   function func(t, u, d) &
         result(du)
      ! calling variables
      real(R8), intent(in) :: t
      real(R8), intent(in) :: u(2)
      real(R8), pointer, intent(in) :: d(:,:)
      real(R8) :: du(2)

      ! ...

   end function func

end module mod_func

program main

   use mod_math, only : rk4, Ifunc_2d
   use mod_func, only : func

   implicit none

   procedure(Ifunc_2d), pointer :: df => null()

   ! this causes the error
   df => func

   ! I've omitted the other variables, because it seems the error

   ! happens on the line above before I even go to the rk4 part.

   ! rk4(df, y0, ti, tf, n, data)

end program main

 

FortranFan
Honored Contributor II
3,222 Views

@da-james ,

By the way, your inquiry is general Fortran that is applicable in the context of more than one Fortran processor e.g., with gfortran as you alluded to as well as any other Fortran compiler in addition to Intel Fortran.

Under the circumstances, you may want to consider posting your inquiries at this Fortran DIscourse site for wider feedback on any and all Fortran-related topics of interest to you:

https://fortran-lang.discourse.group/

You may also ask there regarding any questions with code modernization and Fortran 2018.  You can also inquire there about how to write better general interfaces while keeping in mind the Fortran language standard is decidedly deficient in this aspect and which is a topic for further enhancement in a future revision of the language.

 

 

0 Kudos
da-james
Novice
3,217 Views

Oh, that is my mistake including that parentheses at

df => func()

I actually did write it as

df => func

And after I tried some of your alterations such as using abstract, I'm still getting the same error.

0 Kudos
da-james
Novice
3,198 Views

Actually re-going through the interface, I found a small difference between it and my function.

 

Thank you for the input!

0 Kudos
andrew_4619
Honored Contributor II
3,162 Views

Setting checking options is always a good thing. The original code you posted has some errors as you have found.

ifort mod_func.f90 mod_math.f90 main.f90 /stand /check:all
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.

mod_func.f90(8): warning #6178: The return value of this FUNCTION has not been defined. [DU]
result(du)
-------^
main.f90(11): error #6631: A non-optional actual argument must be present when invoking a procedure with an explicit interface. [T]
df => func()
------^
main.f90(11): error #6631: A non-optional actual argument must be present when invoking a procedure with an explicit interface. [U]
df => func()
------^
main.f90(11): error #6631: A non-optional actual argument must be present when invoking a procedure with an explicit interface. [D]
df => func()
------^
main.f90(11): error #6678: When the target is an expression it must deliver a pointer result.
df => func()
------^
compilation aborted for main.f90 (code 1)

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,169 Views

FF,

RE:

module mod_math
use kinds_m, only : I4, R8
implicit none

abstract interface
function Ifunc_2d(t, y, d) &
result(dy)
import :: I4, R8
...

Could you explain the reason for using "import :: ..." verses "use kinds_m, only : ..." in the abstract interface?

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
3,163 Views

I'll tell you why I prefer IMPORT here - it helps down the road if the source of the definitions changes. It also makes clear that these are the same entities as are visible in the host scope.

I wrote about IMPORT in Domestic or Imported

0 Kudos
FortranFan
Honored Contributor II
3,147 Views
@jimdempseyatthecove wrote:

FF,
..
Could you explain the reason for using "import :: ..." verses "use kinds_m, only : ..." in the abstract interface? ..

 

Jim,

You can see Steve's comment upthread and his blogpost for the technical reasons to prefer the IMPORT statement, particularly the important one that conveys to the processor and to the reader of the code the same entities as in the host scope apply to those in the interface block.

Coders may find these good reasons help them a bit with reduced verbosity as well e.g., entities in the host scope which may be associated across USE multiple statements and separate declarations can all be listed in a simple IMPORT statement in an interface if the coder so wishes i.e., this can also become a stylistic preference.

0 Kudos
Reply