2

I'm having a problem with Fortran and function/subroutine pointers. I have two functions that take an array as an argument. In f1 it is a(n,n), in f2 it's a(n*n). When I call the subroutine manually, I can do this with the same array:

real :: a(5, 5)
call f1(a, 5)
call f2(a, 5)

But when I try to do this with a pointer, the compiler throws it back at me with this error:

ptr => f2
       1
Error: Interface mismatch in procedure pointer assignment at (1): Type/rank missmatch in argument 'a'

Is there a way around this? I was thinking about pointers, but there I have the same problem, to create it I need to know the number of dimensions.

For reference, here's the complete code (I hope it's not too long..)

program ptrtest
implicit none

interface
    subroutine fnc(a, n)
        integer :: n
        real :: a(n, n)
    end subroutine

    subroutine f1(a, n)
        integer :: n
        real :: a(n, n)
    end subroutine

    subroutine f2(a, n)
        integer :: n

        real :: a(n*n)
    end subroutine
end interface

procedure(fnc), pointer :: ptr => null()
real :: a(5, 5)
real :: b(4, 4)

ptr => f1

call ptr(a, 5)
write(*,*) a

!this one does not work..

!ptr => f2
!
!call ptr(b, 4)
!write(*,*) b

call f2(b, 4)
write(*,*) b
end program

subroutine f1(a, n)
integer :: n
real :: a(n, n)
integer :: i

a = 1
end subroutine

subroutine f2(a, n)
integer :: n
real :: a(n*n)

a = 2
end subroutine

I really hope there is a way to do this. I can't really rewrite all the subroutines so the dimensions of the array match every time :/

Regards, Caba

Cabadath
  • 897
  • 3
  • 13
  • 23

4 Answers4

2

Edit: The point is, as written in other answers, that the actual and dummy arguments don't match. Rather than trying for a work around my suggestion is to create a pointer that transforms the rank of the array so that the arguments match. The technique is "pointer bounds remapping" (also see changing array dimensions in fortran.) Here is a more complete example:

module my_subs

contains

subroutine f1 (array)

   real, dimension (:,:), intent (in) :: array
   write (*, *) "f1:", ubound (array, 1), ubound (array, 2)

end subroutine f1


subroutine f2 (array)

   real, dimension (:), intent (in) :: array
   write (*, *) "f2:", ubound (array, 1)

end subroutine f2

end module my_subs


program test_ranks

   use my_subs
   real, dimension (2,2), target :: a2d
   real, dimension (:), pointer :: a4

   a2d = reshape ( [1., 2., 3., 4.], [2,2] )
   call f1 (a2d)

   a4 (1:4) => a2d
   call f2 (a4)

end program test_ranks

I have the subroutines in a module to automatically make the interface explicit -- I believe this the safest practice since it allows the compiler to find argument consistency mistakes and is necessary for "advanced" Fortran 90 features, such as the assumed-shape dimensions (colon) that I used.

Perhaps this example doesn't meets the needs of the question since it doesn't use a pointer to the procedures. It does pass the same array to procedures expecting arrays of differing ranks.

Community
  • 1
  • 1
M. S. B.
  • 28,968
  • 2
  • 46
  • 73
2

You are passing a 2D array (:,:) to a subroutine that expects a 1D (:) array. That's why Fortran complains. One way around it is to write a module with multiple functions with the same name that takes different rank arrays as arguments, like so:

  module test 

  interface fnc1
      module procedure fnc1_1d, fnc1_2d
  end interface

  contains

  subroutine fnc1_1d(ar,b,ar_out)

  real :: ar(:), ar_out(:)
  integer :: b

  ar_out = ar*b

  end subroutine fnc1_1d

  subroutine fnc1_2d(ar,b,ar_out)

  real :: ar(:,:), ar_out(:,:)
  integer :: b

  ar_out = ar*b

  end subroutine fnc1_2d

  end module test

now when you call fnc1, it will call fnc1_1d if you are passing a 1D array, and fnc_2d if you are passing a 2d array.

Program modify_value

use test

implicit none

real :: a1(5), a1_out(5)
real :: a2(5,5), a2_out(5,5)
integer :: j

a1 = 1.
a2 = 2.

call fnc1(a1,4,a1_out)
call fnc1(a2,4,a2_out)

open(1,file="out.txt")

write(1,'(5F5.1)') a1_out
do j=1,5
    write(1,'(5F5.1)') a2_out(j,:)
end do


close(1)

End Program

in out.txt is now:

4.0 4.0 4.0 4.0 4.0

8.0 8.0 8.0 8.0 8.0

8.0 8.0 8.0 8.0 8.0

8.0 8.0 8.0 8.0 8.0

8.0 8.0 8.0 8.0 8.0

8.0 8.0 8.0 8.0 8.0

Hope this helps.

bananafish
  • 2,877
  • 20
  • 29
  • This is an excellent approach if the goal is to select the subroutine based on the rank of the array. But my understanding of the question is that the goal is to pass the array to two subroutines that expect arrays of different ranks, not to automatically select the subroutine of the two with that matches the rank. @Cabadath can clarify if I misunderstand the intent of the question. – M. S. B. Mar 17 '12 at 01:12
  • @M. S. B. you may be right, I may have misunderstood the need a bit. But then wouldn't you just write the second subroutine to accept a 2D array instead of 1D? – bananafish Mar 17 '12 at 01:54
  • @M.S.B, you are right. The point is that I have 3 subroutines, two of which take a 3d array and one that takes a 2d array. There's a loop over ~100^2 where the method is always selected in an if-clause inside the loop. What I wanted to do was "save" the needed subroutine in a pointer (because that does not change inside the loop) and get rid of the if-clause to save some time. – Cabadath Mar 17 '12 at 08:47
  • I doubt that the CPU time of an IF statement branching matters at all in comparison to the time to invoke a subroutine in the branches, and presumedly calculations using the arrays inside the subroutines. If the comparison of the IF statement is complicated you could perform it outside the loop and store the result of the comparison in a logical, though an optimizing compiler is likely to do that for you. The method in my answer will fix the rank mismatch w/o copying the array, which could be expensive. – M. S. B. Mar 17 '12 at 15:58
2

If I change your example program from using explicit interfaces (through the interface block) to using implicit interfaces (through procedure declarations without mentioning an interface), it appears to work for me.

So remove the interface block, slightly alter the declaration of ptr, and add procedure declarations for f1 and f2, like this:

procedure(), pointer :: ptr => null()
procedure() :: f1, f2

(Alternatively, you can use an external statement for f1, and f2, instead of the procedure statement.)

I don't know how feasible this is for the real program, because you may need the explicit interfaces if the actual subroutines use some of the features introduced in Fortran 90 and later.

eriktous
  • 6,569
  • 2
  • 25
  • 35
  • Indeed. I did not know that you can just leave the brackets in the procedure() statement empty. Every example I found had something in them. This makes the whole thing considerably more easy ._. thanks! Now I just have to implement this in the real thing and I hope I'm good to go :) – Cabadath Mar 17 '12 at 08:53
0

In this section:

subroutine f2(a, n) integer :: n real :: a(n*n)

you are using a 1D array when fortran expects a 2D array in the main program.

armando
  • 1,360
  • 2
  • 13
  • 30