过程指针指向接口/重载过程

时间:2014-07-30 00:07:28

标签: pointers interface fortran generic-programming

我正在使用过程重载和接口,以便在Fortran程序中实现某种通用性 为此,我有一个包含许多过程的模块,所有过程都是重复的,以便能够更改变量类型。我还在模块的开头有一系列类型的接口:

    interface norm
      module procedure &
          norm_r8, &
          norm_c
    end interface

现在我的问题是我试图使用过程指针引用norm,因此(在不同的模块中):

procedure(), POINTER :: pNorm => NULL()
pNorm => norm

然而,在这种情况下,gfortran给我一个错误,说我有一个未定义的引用规范。如果我指向norm_r8norm_c,则没问题。但是由于分配指针的代码部分不知道调用norm时将使用的变量类型,我需要指向通用名称!有没有办法指出过载程序?

2 个答案:

答案 0 :(得分:3)

据我所知,不允许过程指针指向通用接口。标准仅提及具有EXTERNAL属性的过程,模块过程或某些内部过程可与过程指针相关联(C1220,ISO / IEC 1539-1:2010)。 Gfortran还会为您的案例发出有用的错误消息:

Error: Procedure pointer target 'norm' at (1) must be either an intrinsic, 
       host or use associated, referenced or have the EXTERNAL attribute

有意义的是,您无法关联到界面,只能关联一个过程。接口仅在procedure(INTERFACE)语句中使用,以提供它可以指向的过程的显式接口。

这不应该是一个showstopper,因为通用接口的目的可以否定你对指针的需求。只要指针用于所有可能的调用在类型,种类,等级和参数数量方面都是唯一的(因此编译器可以区分它们),您可以将它们全部添加到单个通用接口并在其中调用代替指针。或者,您可以使用select type()构造有选择地将指针与类型的特定过程相关联,以避免需要与通用接口关联。


以下是基于参数类型

将指针指定给特定过程的包装程序示例
subroutine get_proc_ptr(pp, arg)
  implicit none
  procedure(), pointer, intent(out) :: pp
  class(*), intent(inout) :: arg

  select type(arg)
    type is (real(kind=kind(1d0)))
       pp => norm_r8
    type is (real)
       pp => norm_r
    type is (integer)
       pp => norm_i
    type is (complex)
       pp => norm_c
    class default
       pp => null()
  end select
end subroutine

可以使用这样的:

real(kind=kind(1d0)) :: arg_r8
procedure(), pointer :: pNorm => null()

arg_r8 = 4.0123456789d30

call get_proc_ptr(pNorm, arg_r8)
call pNorm(arg_r8)

这是一个完整的可编辑示例:

module proc
  implicit none

  interface norm
    module procedure &
      norm_r8, &
      norm_r,  &
      norm_i,  &
      norm_c
  end interface

contains

  subroutine norm_r8(arg)
    implicit none
    real(kind=kind(1d0)), intent(in) :: arg

    write (*,*) "real8: ", arg
  end subroutine

  subroutine norm_r(arg)
    implicit none
    real, intent(in) :: arg

    write (*,*) "real: ", arg
  end subroutine

  subroutine norm_i(arg)
    implicit none
    integer, intent(in) :: arg

    write (*,*) "integer: ", arg
  end subroutine

  subroutine norm_c(arg)
    implicit none
    complex, intent(in) :: arg

    write (*,*) "complex: ", arg
  end subroutine

  subroutine get_proc_ptr(pp, arg)
    implicit none
    procedure(), pointer, intent(out) :: pp
    class(*), intent(inout) :: arg

    select type(arg)
      type is (real(kind=kind(1d0)))
         pp => norm_r8
      type is (real)
         pp => norm_r
      type is (integer)
         pp => norm_i
      type is (complex)
         pp => norm_c
      class default
         pp => null()
    end select
  end subroutine

end module

program test
  use proc
  implicit none
  real(kind=kind(1d0)) :: arg_r8
  real                 :: arg_r
  integer              :: arg_i
  complex              :: arg_c
  procedure(), pointer :: pNorm => null()

  arg_r8 = 4.0123456789d30
  arg_r = 12.5
  arg_i = 56
  arg_c = (34,3)

  call get_proc_ptr(pNorm, arg_r8)
  call pNorm(arg_r8)

  call get_proc_ptr(pNorm, arg_r)
  call pNorm(arg_r)

  call get_proc_ptr(pNorm, arg_i)
  call pNorm(arg_i)

  call get_proc_ptr(pNorm, arg_c)
  call pNorm(arg_c)

end program

这是该程序的输出:

$ ./testprocptr 
 real8:    4.0123456788999999E+030
 real:    12.5000000    
 integer:           56
 complex:  (  34.0000000    ,  3.00000000    )

答案 1 :(得分:0)

如果我理解得很好,你想一次完成两件事。 首先,您希望使用多态来让编译器调用正确的例程,具体取决于您是否具有不同的类型,等级,参数数量等。 其次,您希望使用过程指针在具有相同接口的不同过程之间切换。

我尝试过同样的事情。我没有设法指向一个接口,但我设法用指针创建一个接口。

如果您有这样的模块

module some_module

  ! This is the abstract interface for procedure pointers.
  interface
    subroutine shape_1_interface(arg)
      implicit none
      real, intent(in) :: arg
    end subroutine shape_1_interface
    subroutine shape_2_interface(arg)
      implicit none
      integer, intent(in) :: arg
    end subroutine shape_2_interface
  end interface

contains

  subroutine routine_shape_1_implementation_1(arg)
    implicit none
    real, intent(in) :: arg
    write(*,*) "Arg is real",arg
    write(*,*) "Implementation 1"
  end subroutine routine_shape_1_implementation_1

  subroutine routine_shape_2_implementation_1(arg)
    implicit none
    integer, intent(in) :: arg
    write(*,*) "Arg is int",arg
    write(*,*) "Implementation 1"
  end subroutine routine_shape_2_implementation_1

  subroutine routine_shape_1_implementation_2(arg)
    implicit none
    real, intent(in) :: arg
    write(*,*) "Arg is real",arg
    write(*,*) "Implementation 2"
  end subroutine routine_shape_1_implementation_2

  subroutine routine_shape_2_implementation_2(arg)
    implicit none
    integer, intent(in) :: arg
    write(*,*) "Arg is int",arg
    write(*,*) "Implementation 2"
  end subroutine routine_shape_2_implementation_2

  subroutine routine_shape_1_implementation_3(arg)
    implicit none
    real, intent(in) :: arg
    write(*,*) "Arg is real",arg
    write(*,*) "Implementation 3"
  end subroutine routine_shape_1_implementation_3

  subroutine routine_shape_2_implementation_3(arg)
    implicit none
    integer, intent(in) :: arg
    write(*,*) "Arg is int",arg
    write(*,*) "Implementation 3"
  end subroutine routine_shape_2_implementation_3

end module some_module

然后你可以在你的主程序中做到:

program main

  use some_module

  implicit none

  procedure(shape_1_interface), pointer :: routine_shape_1
  procedure(shape_2_interface), pointer :: routine_shape_2

  interface routine
    procedure routine_shape_1
    procedure routine_shape_2
  end interface routine

  routine_shape_1 => routine_shape_1_implementation_1
  routine_shape_2 => routine_shape_2_implementation_1

  call routine(4)

  routine_shape_1 => routine_shape_1_implementation_2
  routine_shape_2 => routine_shape_2_implementation_2

  call routine(4.0)

end program main

遗憾的是,当您想要将指针设置为不同的实现时,您必须为所有形状执行此操作,但好处是您可以调用'routine'并自动获得所需的功能。

这是输出:

Arg is int           4
Implementation 1
Arg is real   4.00000000000000     
Implementation 2