Fortran过程指向派生类型中子例程的指针

Cha*_*les 5 fortran procedure explicit-interface subroutine

在Fortran中,我需要一个派生类型中的过程指针,它可以指向几个子例程中的一个.这个问题似乎在SO上很常见:

Fortran将过程保存为派生类型的属性

在Fortran 2003中键入绑定过程重载

此类型绑定通用子例程调用没有匹配的特定子例程

带有过程参数的通用类型绑定过程

键入绑定过程作为参数

仅举几例.第一个参考文献非常好地提供了这个函数问题的答案.

但是,在类型绑定过程指针指向子例程的情况下,我仍然不清楚开发此类代码的方法.困难似乎是没有与返回的内容相关联的类型(因为没有真正"返回").

我还想指出的是,虽然一个简单的解决方案可以在FORTRAN的一个较新的标准(2003,2008)的存在,这种解决方案可能无法在所有的编译器,这可能是未来工作的问题的细微差别.所以我对编译器友好的解决方案很感兴趣.

我有一个当前有效的小代码(如下所示),但在我的大代码中,我在文件中遇到内部编译器错误(也显示如下),我在派生类型中使用过程指针.我的问题是:我可以对下面的代码做些什么

1)严格使用显式接口

2)最大化传递给编译器的信息

3)确保代码在尽可能多的编译器之间可移植(即使用fortran 90/95标准).

在多大程度上可以满足上述要求(1是最重要的)?是否有可能满足上述所有标准?我知道"满足所有这些标准"是主观的,但我认为对于关于函数而不是子例程的相同问题,答案是'是'.

 gcc version 5.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project)
Run Code Online (Sandbox Code Playgroud)

小代码:

  module subs_mod
  implicit none
  public :: add,mult
  contains
  subroutine add(x,y,z)
    implicit none
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y+z
  end subroutine
  subroutine mult(x,y,z)
    implicit none
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y*z
  end subroutine
  end module

  module type_A_mod
  use subs_mod
  implicit none
  public :: type_A,init,operate
  type type_A
    procedure(),pointer,nopass :: op
  end type
  contains
  subroutine init(A,op)
    implicit none
    external :: op
    type(type_A),intent(inout) :: A
    A%op => op
  end subroutine
  subroutine operate(A,x,y,z)
    implicit none
    type(type_A),intent(in) :: A
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    call A%op(x,y,z)
  end subroutine
  end module

  program test
  use type_A_mod
  use subs_mod
  implicit none
  type(type_A) :: A
  integer :: x
  call init(A,mult)
  call operate(A,x,3,5)
  write(*,*) 'x = ',x
  end program
Run Code Online (Sandbox Code Playgroud)

大代码中的编译器错误:

    f951.exe: internal compiler error: Segmentation fault
    libbacktrace could not find executable to open
    Please submit a full bug report,
    with preprocessed source if appropriate.
    See <http://sourceforge.net/projects/mingw-w64> for instructions.
Run Code Online (Sandbox Code Playgroud)

UPDATE

这是一个小的修改,为编译器提供了更多信息,但我没有在大代码上尝试过这个.然而,它似乎是武断的,我不知道它是否会有所帮助.

  ...
  function add(x,y,z) result(TF)
  ...
    logical :: TF
    x = y+z
    TF = .true.
  end function
  function mult(x,y,z) result(TF)
  ...
    logical :: TF
    x = y*z
    TF = .true.
  end function
  end module

  module type_A_mod
  ...
  type type_A
    procedure(logical),pointer,nopass :: op
  end type
  ...
  subroutine init(A,op)
    implicit none
    logical,external :: op
  ...
  end subroutine
  subroutine operate(A,x,y,z)
  ...
    logical :: TF
    TF = A%op(x,y,z)
  end subroutine
  end module

  program test
  ...
  end program
Run Code Online (Sandbox Code Playgroud)

解决方案评论 只是评论解决方案(由@IanH提供):还有一个额外的皱纹,那就是我有一些派生类型进入抽象接口,根据Fortran 2003的新功能,该Import声明应该包括在内使抽象接口知道任何输入的派生类型.这是一个小的工作示例,应用于大代码,减轻了我正在使用的内部编译器错误:)

  module DT_mod
  implicit none
  private
  public :: DT
  type DT
    integer :: i
  end type
  contains
  end module

  module subs_mod
  use DT_mod
  implicit none
  private
  public :: add,mult,op_int

  abstract interface
  subroutine op_int(d,x,y,z)
    import :: DT
    implicit none
    type(DT),intent(inout) :: d
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
  end subroutine
  end interface

  contains
  subroutine add(d,x,y,z)
    implicit none
    type(DT),intent(inout) :: d
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y+z
    d%i = 1
  end subroutine
  subroutine mult(d,x,y,z)
    implicit none
    type(DT),intent(inout) :: d
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y*z
    d%i = 2
  end subroutine
  end module

  module type_A_mod
  use DT_mod
  use subs_mod
  implicit none
  private
  public :: type_A,init,operate
  type type_A
    procedure(op_int),pointer,nopass :: op
  end type
  contains
  subroutine init(A,op)
    implicit none
    procedure(op_int) :: op
    type(type_A),intent(inout) :: A
    A%op => op
  end subroutine
  subroutine operate(A,d,x,y,z)
    implicit none
    type(DT),intent(inout) :: d
    type(type_A),intent(in) :: A
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    call A%op(d,x,y,z)
  end subroutine
  end module

  program test
  use type_A_mod
  use subs_mod
  use DT_mod
  implicit none
  type(type_A) :: A
  type(DT) :: d
  integer :: x,y,z
  y = 3; z = 5
  call init(A,mult)
  call operate(A,d,x,y,z)
  write(*,*) 'x,y,x = ',y,z,x
  write(*,*) 'd%i = ',d%i
  end program
Run Code Online (Sandbox Code Playgroud)

任何帮助是极大的赞赏.

Ian*_*anH 6

在Fortran 2003之前,过程指针不是标准语言的一部分,所以如果你想要使用它们,那么Fortran 95的兼容性是无关紧要的.

无论提供给编译器的源是什么,内部编译器错误都是编译器的错误.

没有类型绑定过程指针这样的东西.你有一个类型绑定过程 - 这是在派生类型构造中的CONTAINS之后声明的事物,或者你有一个过程指针 - 它可以是一个类型的组件或一个独立的对象.作为组件的过程指针是派生类型的对象的值的一部分 - 它可以在运行时与不同的过程相关联.类型绑定过程是类型声明的固定属性.

如果希望过程指针(或虚拟过程)具有显式接口,则必须在过程声明语句的括号内提供接口名称.

procedure(interface_name_goes_here) [, pointer, ...] :: thing_being_declared
Run Code Online (Sandbox Code Playgroud)

提供的接口名称可以是可访问的特定过程的名称(包括先前由不同过程声明语句声明的过程),或抽象接口的名称.

(如果过程声明语句中的接口名称是类型,就像示例代码中的组件一样,声明的过程是具有给定类型结果的函数,具有隐式接口.

如果过程声明语句中的接口名称完全丢失,则声明的过程可能是一个函数或子例程(其后续使用必须与其中一个一致)和一个隐式接口.)

因此,假如你想用显式接口功能(违背了问题的标题)具有相同特性来声明一个过程指针组件addmult在你的代码的第二伸展:

TYPE type_A
  PROCEDURE(the_interface), POINTER, NOPASS :: op
END TYPE type_A

ABSTRACT INTERFACE
  FUNCTION the_interface(x, y, z) RESULT(tf)
    IMPLICIT NONE
    ! function modifying arguments - poor style!!!
    INTEGER, INTENT(INOUT) :: x
    INTEGER, INTENT(IN) :: y, z
    LOGICAL :: tf
  END FUNCTION the_interface
END INTERFACE
Run Code Online (Sandbox Code Playgroud)

如果您希望过程指针是具有显式接口的子例程(这比修改其参数的函数更好) - 适当地更改抽象接口.

init子例程中的虚拟过程不必是指针 - 在内部,init您不会更改op引用的内容 - 您只是将另一个指针指向它:

PROCEDURE(the_interface) :: op
Run Code Online (Sandbox Code Playgroud)

当使用显式接口声明虚拟过程和过程指针时,我希望合理的编译器能够诊断出特性中的任何不匹配.