指向派生类型子例程的 Fortran 过程指针 [英] Fortran procedure pointer to subroutines in derived type

查看:21
本文介绍了指向派生类型子例程的 Fortran 过程指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In Fortran, I need a procedure pointer inside a derived type that can point to one of several subroutines. This problem seems to be common on SO:

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

Fortran 2003 中的类型绑定过程重载

没有匹配的具体此类型绑定的通用子程序调用的子程序

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

类型绑定过程作为参数

仅举几例.functions 这个问题的答案在第一个参考文献中提供了很好的答案.

to name a few. The answer to this question for functions is provided very nicely in the first reference.

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

However, I'm still not clear on a methodology to develop such code well in the case that the type-bound procedure pointer is pointing to a subroutine. The difficulty seems to be that there is no type associated with what is returned (since nothing is really "returned").

我还想指出一个细微差别,尽管在较新的 fortran 标准(2003,2008)中可能存在一个简单的解决方案,但该解决方案可能不适用于所有编译器,这在未来可能会出现问题.所以我对编译器友好的解决方案很感兴趣.

I would also like to point out the nuance that, although a simple solution may exist in a more recent standard of fortran (2003,2008), this solution may not work on all compilers, which may be problematic in the future. So I'm interested in compiler-friendly solutions.

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

I have a small code (shown below) that currently works, but in my big code, I'm getting an internal compiler error (also shown below) in the file where I use procedure pointers in derived types. My question is: what can I do to the code below to

1) 严格使用显式接口

1) Strictly use explicit interfaces

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

2) Maximize information passed to the compiler

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

3) Ensure the code is portable between as many compilers as possible (i.e. use fortran 90 / 95 standards).

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

To what degree can the above be satisfied (1 being most important)? Is it possible to satisfy all of these criteria above? I know that's "satisfy all of these criteria" is subjective, but I would argue that the answer is 'yes' for the same question regarding functions instead of subroutines.

 gcc version 5.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project)

小代码:

  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

大代码中的编译器错误:

Compiler error in big code:

    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.

更新

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

Here's a small modification that gives the compiler more information, but I have not tried this on the big code. However, it seems arbitrary, and I have no idea if it will help or not.

  ...
  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

解决方案评论只是评论解决方案(由@IanH 提供):还有一个额外的问题,那就是我有一些派生类型进入抽象接口,根据 Fortran 2003 的新特性,应包含 Import 语句以使抽象接口感知任何输入的派生类型.这是一个小的工作示例,它应用于大代码,减轻了我遇到的内部编译器错误:)

SOLUTION COMMENTS Just to comment on the solution (provided by @IanH): there was one additional wrinkle, and that was that I had some derived types entering the abstract interface, which according to The New Features of Fortran 2003, the Import statement should be included to make the abstract interface aware of any entering derived types. Here is a small working example, which, applied to the big code, mitigates the internal compiler error I was having :)

  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

非常感谢任何帮助.

推荐答案

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

Procedure pointers were not part of the standard language until Fortran 2003, so if you want to use them at all, then Fortran 95 compatibility is irrelevant.

内部编译器错误是编译器的错误,与提供给编译器的源无关.

An internal compiler error is a error with the compiler, regardless of the source provided to the compiler.

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

There is no such thing as a type bound procedure pointer. You either have a type bound procedure - which is a thing declared after the CONTAINS in a derived type construct, or you have a procedure pointer - which can be a component of a type or a stand-alone object. A procedure pointer that is a component is part of the value of an object of the derived type - it can be associated with different procedures at runtime. A type bound procedure is a fixed property of the type declaration.

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

If you want a procedure pointer (or dummy procedure) to have an explicit interface, then you must provide an interface name inside the parenthesis of the procedure declaration statement.

procedure(interface_name_goes_here) [, pointer, ...] :: thing_being_declared

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

The interface name provided can be the name of an accessible specific procedure (including one previously declared by a different procedure declaration statement), or the name of an abstract interface.

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

(If the interface name in a procedure declaration statement is a type, as it is for the component in your example code, the procedure that is declared is a function with a result of the given type, with an implicit interface.

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

If the interface name in a procedure declaration statement is completely missing, the procedure that is declared may be a function or subroutine (its subsequent use in that must be consistent with one or the other) with an implicit interface.)

所以假设你要声明一个过程指针组件,它具有一个函数的显式接口(与问题标题相反),具有与 addmult 相同的特征你的第二段代码:

So assuming you want to declare a procedure pointer component with an explicit interface to a function (contrary to the question title) with the same characteristics as add or mult in your second stretch of code:

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

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

If you want the procedure pointer to be a subroutine with an explicit interface (which is preferable to a function that modifies its arguments) - change the abstract interface appropriately.

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

The dummy procedure in the init subroutine does not have to be a pointer - inside init you are not changing what the op thing references - you are merely pointing another pointer at it:

PROCEDURE(the_interface) :: op

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

When your dummy procedures and procedure pointers are declared with an explicit interface, I would expect a reasonable compiler to diagnose any mismatches in characteristics.

这篇关于指向派生类型子例程的 Fortran 过程指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆