【问题标题】:Fortran 77 code with incompatible calling sequences - how to modularize in Fortran 90/95?调用序列不兼容的 Fortran 77 代码 - 如何在 Fortran 90/95 中模块化?
【发布时间】:2015-08-05 18:58:44
【问题描述】:

考虑以下“现实世界”的遗留 Fortran 77 代码,根据标准,它很可能是非法的,但在现实生活中适用于各种编译器,并且如果每个子例程单独编译,则不会产生编译器或链接器警告.

subroutine a
complex z(10)
call b(z)
call r1(z)
call r2(z)
end

subroutine b(z)
complex z(10)
c ... use complex arithmetic on z
end

subroutine r1(x)
real x(2,10)
c ... do something with real and imaginary parts
c ... real parts are x(1,*)
c ... imaginary parts are x(2,*)
end

subroutine r2(x)
real x(20)
c ... do something with real and imaginary parts
end

我想使用 Fortran 90/95 模块以这种风格重新打包代码。

的幼稚方法
module m
public a
private b, r1, r2
contains 

subroutine a
complex z(10)
call b(z)
call r1(z)
call r2(z) 
end subroutine a

subroutine b(z)
complex z(10)
c ... use complex arithmetic on z
end subroutine b

subroutine r1(x)
real x(2,10)
c ... do something with real and imaginary parts
c ... real parts are x(1,*)
c ... imaginary parts are x(2,*)
end subroutine r1

subroutine r2(x)
real x(20)
c ... do something with real and imaginary parts
end subroutine r2

end module m

不编译,因为(当然)编译器现在可以看到子例程 r1 和 r2 使用错误的参数类型调用。

我需要一些关于如何解决这个问题的想法,只需对现有代码进行最少的重写,并且不复制内存中的数据 - 数据的真实大小太大了。

【问题讨论】:

  • 我的建议是在将代码从旧标准移植到现代标准时真正清理代码,除非有很强的时间限制。由于这些子例程无论如何都是私有的,因此进入它,将参数更改为复杂的。必要时将 1 个循环更改为 2 个。从长远来看,它会有所回报。
  • 在理想情况下,我会同意你的观点,但是重写几十万行记录不佳的遗留代码(可以追溯到“结构化编程尚未发明”的时代)并不是一个有吸引力的选择在现实世界中。

标签: fortran fortran90 gfortran fortran77


【解决方案1】:

c_f_pointer() 可能有助于获取指向complex(*) 数组的real(2,*) 指针,但我不确定是否将复杂参数 (zconst(:)) 传递给 ctor() 是否真的可以......(这里我使用了 gfortran 4.4 & 4.8 和 ifort 14.0,为了使输出更紧凑,数组维度从 10 更改为 3。)

module m
    implicit none
contains

subroutine r1 ( x )
    real x( 2, 3 )

    print *
    print *, "In r1:"
    print *, "Real part = ",x( 1, : )
    print *, "Imag part = ",x( 2, : )
endsubroutine

subroutine r2 ( x )
    real x( 6 )

    print *
    print *, "In r2:"
    print *, "all elements = ", x( : )
endsubroutine

subroutine b ( z )
    complex :: z( 3 )
    real, pointer :: rp(:,:)

    rp => ctor( z, 3 )  !! to compare z and rp                                      
    print *
    print *, "In b:"
    print *, "1st elem = ", z( 1 ), rp( :, 1 )
    print *, "3rd elem = ", z( 3 ), rp( :, 3 )
endsubroutine

function ctor( z, n ) result( ret )   !! get real(2,*) pointer to complex(*)
    use iso_c_binding
    implicit none
    integer :: n
    complex, target :: z( n )
    real, pointer :: ret(:,:)
    call c_f_pointer( c_loc(z(1)), ret, shape=[2,n] )
endfunction

endmodule

program main
    use m
    implicit none
    complex z(3)
    complex, parameter :: zconst(3) = [(7.0,-7.0),(8.0,-8.0),(9.0,-9.0)]

    z(1) = ( 1.0, -1.0 )
    z(2) = ( 2.0, -2.0 )
    z(3) = ( 3.0, -3.0 )

    call r1 ( ctor( z, 3 ) )
    call r1 ( ctor( zconst, 3 ) )

    call r2 ( ctor( z, 3 ) )
    call r2 ( ctor( zconst, 3 ) )

    call b ( z )
    call b ( zconst )
 endprogram

【讨论】:

  • 天哪,问题的标题是“Fortran90/95”...如果不合适,请告诉我。
  • “等价”答案让我想起了非标准的“Cray Fortran 指针”语法,该语法被广泛实施以实现兼容性,并且与您的答案非常相似,但代码比您的 c_f_pointer 少解决方案。我现在将问题悬而未决,看看是否有其他想法出现。我说 Fortran 90/95 是因为在 90/95 中模块化代码时检查的参数列表是问题的根本原因。 Fortran 2003 解决方案是可以的。
  • ctorz 虚拟参数关联的实际参数没有目标属性。当函数执行完成时,与虚拟参数关联的任何指针的指针关联状态变为未定义 - 即无法保证函数完成时 ret 指向您认为它指向的东西。此外,此代码可能违反 C_F_POINTER 参数的限制,具体取决于事物是否可互操作。
  • @IanH 评论的第一部分:stackoverflow.com/q/31345009
  • 感谢 cmets。因此,在这种情况下,只需将 TARGET 属性附加到调用方中的实际(复杂)参数(如子程序 b() 中的 z)或主程序中的局部变量 z(3) 就足够了,以便使符合 Fortran 2003 标准的代码?
【解决方案2】:

请注意,以下建议有一些倒退的方面。

在问题的示例代码中,数据的原始来源是一个局部变量。这样的局部变量可以通过使用等价语句出现在存储关联上下文中,并且在这样的上下文中可以将 COMPLEX 对象视为一对 REAL 对象。

module m
  public a
  private b, r1, r2
contains 
  subroutine a
    complex z(10)
    real r(size(z)*2)
    equivalence (z,r)
    call b(z)     ! pass complex array
    call r1(r)    ! pass real array
    call r2(r)    ! pass real  array
  end subroutine a

  subroutine b(z)
    complex z(10)
    ! ... use complex arithmetic on z
  end subroutine b

  subroutine r1(x)
    real x(2,10)
    ! ... do something with real and imaginary parts
    ! ... real parts are x(1,*)
    ! ... imaginary parts are x(2,*)
  end subroutine r1

  subroutine r2(x)
    real x(20)
    ! ... do something with real and imaginary parts
  end subroutine r2
end module m

【讨论】:

  • 这对于部分解决方案来说是一个有用的想法,但如果将变量 z 作为参数传递给子例程 a,它就不起作用。很抱歉忘记了原始问题中的用例!
  • 虚拟参数不能参与存储关联上下文(尽管相关的有效参数可能在这样的上下文中)。您需要将与z 对应的原始对象通过其他方式(可能作为模块变量或通过 COMMON)放入您的示例中。
【解决方案3】:

(这可能更像是一个评论而不是一个答案,但你不能在评论中包含格式化的代码)。

@roygvib 在标准 fortran 2003 中回答了这个问题。

可以使用许多编译器中实现的非标准“Cray Fortran 指针”语法更简洁地编码相同的想法 - 例如 gfortran 中的 -fcray-pointer 选项。非标准内部函数loc 替换ctor

subroutine b ( z )
    complex :: z( 3 )

    real :: r( 2, 3 )
    pointer(zp, r)
    zp = loc(z)

    print *
    print *, "In b:"
    print *, "1st elem = ", z( 1 ), r( :, 1)
    print *, "3rd elem = ", z( 3 ), r( :, 3)
endsubroutine

【讨论】:

  • 是的,Cray 指针非常紧凑,如果它可以用于此目的,它看起来非常漂亮。我多次尝试学习 Cray 指针,但每次都放弃了,因为我找不到好的教程页面……(最终,Fortran 指针出现在标准中)。但是恕我直言,我希望在 Fortran 中也有一些更灵活的“类型转换”语法(尽管我猜它是故意不由标准提供的)。
  • Cray 指针在具有字可寻址硬件的原始 Cray 上更好,因此 zp = zp + 1 与 C 中的 zp++ 含义相同。具有讽刺意味的是,我知道 Cray 指针早在1980 年代,但是当我遇到这个问题时我已经忘记了它们,直到答案提醒我!
猜你喜欢
  • 2013-06-27
  • 2023-03-27
  • 1970-01-01
  • 2015-09-20
  • 1970-01-01
  • 2012-11-30
  • 1970-01-01
  • 1970-01-01
  • 2016-02-17
相关资源
最近更新 更多