指向派生类型子例程的 Fortran 过程指针
Fortran procedure pointer to subroutines in derived type
在 Fortran 中,我需要派生类型中的过程指针,它可以指向多个子例程之一。这个问题似乎在 SO:
上很常见
Type bound procedure overloading in Fortran 2003
There is no matching specific subroutine for this type bound generic subroutine call
Generic type-bound procedures with procedure arguments
Type bound procedure as arguments
仅举几例。 functions 这个问题的答案在第一篇参考文献中提供得非常好。
但是,在类型绑定过程指针指向子例程的情况下,我仍然不清楚开发此类代码的方法。困难似乎是没有与返回的内容相关联的类型(因为没有什么是真正的 "returned")。
我还想指出一个细微差别,尽管在更新的 fortran 标准 (2003,2008) 中可能存在一个简单的解决方案,但该解决方案可能不适用于所有编译器,这在未来。所以我对编译器友好的解决方案很感兴趣。
我有一个目前可以运行的小代码(如下所示),但在我的大代码中,我在派生类型中使用过程指针的文件中出现内部编译器错误(也如下所示)。我的问题是:我可以对下面的代码做什么
1) 严格使用显式接口
2) 最大化传递给编译器的信息
3) 确保代码在尽可能多的编译器之间可移植(即使用 fortran 90 / 95 标准)。
以上能满足到什么程度(1最重要)?是否有可能满足以上所有这些标准?我知道 "satisfy all of these criteria" 是主观的,但我认为对于关于函数而不是子例程的相同问题,答案是 'yes'。
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
大代码编译错误:
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.
更新
这里有一个小修改,可以为编译器提供更多信息,但我没有在大代码上尝试过。然而,这似乎是随意的,我不知道它是否有帮助。
...
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 提供):还有一个问题,那就是我有一些派生类型进入抽象接口,根据 The New Features of 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
非常感谢任何帮助。
在 Fortran 2003 之前,过程指针不是标准语言的一部分,因此如果您想使用它们,那么与 Fortran 95 的兼容性就无关紧要了。
内部编译器错误是编译器的错误,与提供给编译器的源代码无关。
没有类型绑定过程指针这样的东西。你要么有一个类型绑定过程——它是在派生类型构造中的 CONTAINS 之后声明的东西,要么你有一个过程指针——它可以是类型或 stand-alone object 的组件。作为组件的过程指针是派生类型的 object 值的一部分 - 它可以在运行时与不同的过程相关联。类型绑定过程是类型声明的固定 属性。
如果您希望过程指针(或虚拟过程)具有显式接口,则必须在过程声明语句的括号内提供接口名称。
procedure(interface_name_goes_here) [, pointer, ...] :: thing_being_declared
提供的接口名称可以是可访问的特定过程的名称(包括以前由不同的过程声明语句声明的),也可以是抽象接口的名称。
(如果过程声明语句中的接口名称是一个类型,就像示例代码中的组件一样,则声明的过程是一个具有给定类型结果的函数,具有隐式接口.
如果过程声明语句中的接口名称完全缺失,则声明的过程可能是具有隐式接口的函数或子程序(其后续使用必须两者一致)。
因此,假设您想声明一个过程指针组件,该组件具有与函数的显式接口(与问题标题相反),其特征与第二段中的 add
或 mult
相同代码:
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
如果您希望过程指针成为具有显式接口的子例程(这比修改其参数的函数更可取)- 适当更改抽象接口。
init
子例程中的虚拟过程不一定是指针 - 在 init
中你没有改变 op
引用的东西 - 你只是指向另一个指针在它:
PROCEDURE(the_interface) :: op
当使用显式接口声明虚拟过程和过程指针时,我希望合理的编译器能够诊断特征中的任何不匹配。
这是我的工作示例:
module obj_mod
integer, parameter :: n = 5
type obj_type
procedure(sub_interface), pointer, nopass :: obj_sub => NULL()
end type
interface
subroutine sub_interface(y, x)
import n
double precision, dimension(n) :: x, y
end subroutine sub_interface
end interface
contains
subroutine sq_sub(x, y)
double precision, dimension(n) :: x, y
y = x ** 2
end subroutine
subroutine exp_sub(x, y)
double precision, dimension(n) :: x, y
y = exp(x)
end subroutine
end module
program member_subroutine
use obj_mod
type(obj_type) obj
double precision, dimension(n) :: x, y
x = (/ 1, 2, 3, 4, 5 /)
write(*,*) 'x =', x
obj%obj_sub => sq_sub
call obj%obj_sub(x, y)
write(*,*) 'y1 =', y
obj%obj_sub => exp_sub
call obj%obj_sub(x, y)
write(*,*) 'y2 =', y
end program member_subroutine
在 Fortran 中,我需要派生类型中的过程指针,它可以指向多个子例程之一。这个问题似乎在 SO:
上很常见Type bound procedure overloading in Fortran 2003
There is no matching specific subroutine for this type bound generic subroutine call
Generic type-bound procedures with procedure arguments
Type bound procedure as arguments
仅举几例。 functions 这个问题的答案在第一篇参考文献中提供得非常好。
但是,在类型绑定过程指针指向子例程的情况下,我仍然不清楚开发此类代码的方法。困难似乎是没有与返回的内容相关联的类型(因为没有什么是真正的 "returned")。
我还想指出一个细微差别,尽管在更新的 fortran 标准 (2003,2008) 中可能存在一个简单的解决方案,但该解决方案可能不适用于所有编译器,这在未来。所以我对编译器友好的解决方案很感兴趣。
我有一个目前可以运行的小代码(如下所示),但在我的大代码中,我在派生类型中使用过程指针的文件中出现内部编译器错误(也如下所示)。我的问题是:我可以对下面的代码做什么
1) 严格使用显式接口
2) 最大化传递给编译器的信息
3) 确保代码在尽可能多的编译器之间可移植(即使用 fortran 90 / 95 标准)。
以上能满足到什么程度(1最重要)?是否有可能满足以上所有这些标准?我知道 "satisfy all of these criteria" 是主观的,但我认为对于关于函数而不是子例程的相同问题,答案是 'yes'。
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
大代码编译错误:
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.
更新
这里有一个小修改,可以为编译器提供更多信息,但我没有在大代码上尝试过。然而,这似乎是随意的,我不知道它是否有帮助。
...
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 提供):还有一个问题,那就是我有一些派生类型进入抽象接口,根据 The New Features of 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
非常感谢任何帮助。
在 Fortran 2003 之前,过程指针不是标准语言的一部分,因此如果您想使用它们,那么与 Fortran 95 的兼容性就无关紧要了。
内部编译器错误是编译器的错误,与提供给编译器的源代码无关。
没有类型绑定过程指针这样的东西。你要么有一个类型绑定过程——它是在派生类型构造中的 CONTAINS 之后声明的东西,要么你有一个过程指针——它可以是类型或 stand-alone object 的组件。作为组件的过程指针是派生类型的 object 值的一部分 - 它可以在运行时与不同的过程相关联。类型绑定过程是类型声明的固定 属性。
如果您希望过程指针(或虚拟过程)具有显式接口,则必须在过程声明语句的括号内提供接口名称。
procedure(interface_name_goes_here) [, pointer, ...] :: thing_being_declared
提供的接口名称可以是可访问的特定过程的名称(包括以前由不同的过程声明语句声明的),也可以是抽象接口的名称。
(如果过程声明语句中的接口名称是一个类型,就像示例代码中的组件一样,则声明的过程是一个具有给定类型结果的函数,具有隐式接口.
如果过程声明语句中的接口名称完全缺失,则声明的过程可能是具有隐式接口的函数或子程序(其后续使用必须两者一致)。
因此,假设您想声明一个过程指针组件,该组件具有与函数的显式接口(与问题标题相反),其特征与第二段中的 add
或 mult
相同代码:
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
如果您希望过程指针成为具有显式接口的子例程(这比修改其参数的函数更可取)- 适当更改抽象接口。
init
子例程中的虚拟过程不一定是指针 - 在 init
中你没有改变 op
引用的东西 - 你只是指向另一个指针在它:
PROCEDURE(the_interface) :: op
当使用显式接口声明虚拟过程和过程指针时,我希望合理的编译器能够诊断特征中的任何不匹配。
这是我的工作示例:
module obj_mod
integer, parameter :: n = 5
type obj_type
procedure(sub_interface), pointer, nopass :: obj_sub => NULL()
end type
interface
subroutine sub_interface(y, x)
import n
double precision, dimension(n) :: x, y
end subroutine sub_interface
end interface
contains
subroutine sq_sub(x, y)
double precision, dimension(n) :: x, y
y = x ** 2
end subroutine
subroutine exp_sub(x, y)
double precision, dimension(n) :: x, y
y = exp(x)
end subroutine
end module
program member_subroutine
use obj_mod
type(obj_type) obj
double precision, dimension(n) :: x, y
x = (/ 1, 2, 3, 4, 5 /)
write(*,*) 'x =', x
obj%obj_sub => sq_sub
call obj%obj_sub(x, y)
write(*,*) 'y1 =', y
obj%obj_sub => exp_sub
call obj%obj_sub(x, y)
write(*,*) 'y2 =', y
end program member_subroutine