Указатели подпрограмм Fortran для несоответствия размеров массива - PullRequest
2 голосов
/ 16 марта 2012

У меня проблема с фортраном и указателями функций / подпрограмм. У меня есть две функции, которые принимают массив в качестве аргумента. В f1 это (n, n), в f2 это (n * n). Когда я вызываю подпрограмму вручную, я могу сделать это с тем же массивом:

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

Но когда я пытаюсь сделать это с указателем, компилятор возвращает его мне с этой ошибкой:

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

Есть ли способ обойти это? Я думал об указателях, но там у меня та же проблема, чтобы создать ее, мне нужно знать количество измерений.

Для справки вот полный код (надеюсь, он не слишком длинный ..)

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

Я действительно надеюсь, что есть способ сделать это. Я не могу переписать все подпрограммы, поэтому размеры массива совпадают каждый раз: /

С уважением, Каба

Ответы [ 4 ]

2 голосов
/ 17 марта 2012

Если я изменю вашу примерную программу с использования явных интерфейсов (через блок интерфейса) на использование неявных интерфейсов (через объявления процедур без упоминания интерфейса), это, похоже, будет работать для меня.block, слегка измените объявление ptr и добавьте объявления процедур для f1 и f2, например:

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

(В качестве альтернативы вы можете использовать оператор external для f1 и f2 вместо оператора процедуры.)

Я не знаю, насколько это возможно для реальной программы, потому что вам могут понадобиться явные интерфейсы, если фактические подпрограммы используют некоторые функциивведено в Фортран 90 и позже.

2 голосов
/ 17 марта 2012

Вы передаете 2D-массив (:, :) в подпрограмму, которая ожидает 1D (:)-массив. Вот почему Фортран жалуется. Одним из способов решения этой проблемы является написание модуля с несколькими функциями с одинаковым именем, который принимает разные ранговые массивы в качестве аргументов, например:

  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

теперь, когда вы вызываете fnc1, он будет вызывать fnc1_1d, если вы передаете 1D массив, и fnc_2d, если вы передаете 2d массив.

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

в out.txt сейчас:

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

Надеюсь, это поможет.

2 голосов
/ 17 марта 2012

Редактировать: Дело в том, что, как написано в других ответах, фактические и фиктивные аргументы не совпадают.Вместо того, чтобы пытаться обойти эту проблему, я предлагаю создать указатель, который преобразует ранг массива так, чтобы аргументы совпадали.Техника «переназначение границ указателя» (также см. изменение размеров массива в fortran .) Вот более полный пример:

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

У меня есть подпрограммы в модуле, чтобы автоматически сделатьявный интерфейс - я считаю, что это самая безопасная практика, поскольку она позволяет компилятору находить ошибки согласованности аргументов и необходима для «продвинутых» функций Fortran 90, таких как измерения предполагаемой формы (двоеточие), которые я использовал.

Возможно, этот пример не соответствует потребностям вопроса, так как он не использует указатель на процедуры.Он передает тот же массив процедурам, ожидающим массивы разных рангов.

0 голосов
/ 17 марта 2012

В этом разделе:

подпрограмма f2 (a, n) integer :: n real :: a (n * n)

вы используете одномерный массив, когда Fortran ожидает 2Dмассив в основной программе.

...