【问题标题】:Difference between Fortran's "abstract" and "normal" interfacesFortran 的“抽象”和“普通”接口之间的区别
【发布时间】:2016-05-06 05:44:22
【问题描述】:

我试图理解抽象接口和“普通”接口之间的区别。是什么让接口抽象?什么时候需要?

假设下面的例子

module abstract_type_mod
  implicit none

  type, abstract :: abstract_t
  contains
    procedure(abstract_foo), pass, deferred :: Foo
  end type

  interface
    subroutine abstract_foo ( this, a, b )
      import :: abstract_t
      implicit none
      class(abstract_t), intent(in)  :: this
      real,              intent(in)  :: a
      real,              intent(out) :: b
    end subroutine
  end interface

end module

module concrete_type_mod
  use abstract_type_mod
  implicit none

  type, extends ( abstract_t ) :: concrete_t
  contains
    procedure, pass :: Foo
  end type

  contains

  subroutine Foo ( this, a, b )
    implicit none
    class(concrete_t), intent(in)  :: this
    real,              intent(in)  :: a
    real,              intent(out) :: b

    b = 2 * a

  end subroutine
end module 

module ifaces_mod
  implicit none

  interface
    subroutine foo_sub ( a, b )
      implicit none
      real, intent(in)  :: a
      real, intent(out) :: b
    end subroutine
  end interface

end module

module subs_mod
  implicit none

  contains

  pure subroutine module_foo ( a, b )
    implicit none
    real, intent(in)  :: a
    real, intent(out) :: b

    b = 2 * a

  end subroutine

end module

program test
  use ifaces_mod
  use subs_mod
  use concrete_type_mod
  implicit none

  type(concrete_t) :: concrete
  procedure(foo_sub) :: external_sub
  procedure(foo_sub), pointer :: foo_ptr
  real :: b

  foo_ptr => external_sub

  call foo_ptr ( 0.0, b )
  print*, b

  foo_ptr => module_foo

  call foo_ptr ( 1.0, b )
  print*, b

  call concrete%Foo ( 1.0, b )
  print*, b

end program

pure subroutine external_sub ( a, b )
  implicit none
  real, intent(in)  :: a
  real, intent(out) :: b

  b = a + 5

end subroutine

输出是

5.000000
2.000000
2.000000

我没有在这里使用抽象接口。至少我认为我没有?我已经这样做了一段时间,而且我从来没有在接口上使用过抽象的“限定符”。好像没有找到需要使用抽象接口的案例。

有人可以在这里启发我吗?

PS:编译器 Intel Visual Fortran Composer XE 2013 SP1 更新 3。


编辑:

在现代 Fortran 中引用 Metcalf、Reid 和 Cohen 的解释:

在 Fortran 95 中,使用 显式接口,需要使用接口块。这可以 对于单个过程,但对于声明多个过程有点冗长 具有相同接口的程序(除了程序 名称)。此外,在 Fortran 2003 中,有几种情况 这变得不可能(过程指针组件或 抽象类型的绑定过程)。

那么,我的编译器是否错误地接受了下面的代码以及上面的抽象类型的代码?

module ifaces_mod
  implicit none

  interface
    subroutine foo_sub ( a, b )
      implicit none
      real, intent(in)  :: a
      real, intent(out) :: b
    end subroutine
  end interface

end module

module my_type_mod
  use ifaces_mod
  implicit none

  type my_type_t
    procedure(foo_sub), nopass, pointer :: Foo => null()  
  end type

end module

在这两种情况下,我会说我实际上已经声明了抽象接口,而没有使用 abstract 关键字。我认为我的困惑源于编译器接受这样的代码这一事实。

【问题讨论】:

    标签: interface fortran


    【解决方案1】:

    “正常”接口——被标准称为特定接口块(正如您在问题标题中使用的那样)——只是某些过程的正常接口块。因此:

    interface
      subroutine foo_sub
      end subroutine
    end interface
    

    表示存在一个名为foo_sub的实际(外部)子程序,它符合指定的接口。

    抽象接口

    abstract interface
      subroutine foo_sub_abs
      end subroutine
    end interface
    

    只是指定某些过程的外观,但名称是接口的名称,而不是任何实际过程的名称。它可以用于过程指针

    procedure(foo_sub_abs), pointer :: p
    

    或用于虚拟参数

    subroutine bar(f)
      procedure(foo_sub_abs) :: f
    

    这意味着p将指向或作为f传递的实际过程符合抽象接口。

    请注意,在前两个示例中,您可以使用一些现有的过程而不是抽象接口。它只需要在作用域内有可用的显式接口(通常在同一个模块中,或者在使用的模块中)。


    据我所知(但请参阅下面@IanH 的评论)编译器可以拒绝您的代码:

      interface
        subroutine abstract_foo ( this, a, b )
          import :: abstract_t
          implicit none
          class(abstract_t), intent(in)  :: this
          real,              intent(in)  :: a
          real,              intent(out) :: b
        end subroutine
      end interface
    

    因为不存在名为abstract_foo 的实际过程。一些编译器不会对此进行诊断,但他们可以。


    完全不相关的是通用接口。你可以认出它们,因为在interface这个词后面有一个通用过程的名称

      interface generic_sub
        procedure sub1
    
        subroutine sub2(...)
        end subroutine
      end interface
    

    这里sub1sub2都存在,sub1是已知的并且已经有明确的接口可用,sub2是外部的并且看起来是接口指定的,两者都是通用generic_sub的特定过程.这是完全不同的用法。

    然后你打电话

    call generic_sub(...)
    

    并根据您传递的参数,编译器选择调用哪个特定过程,如果是sub1,还是sub2

    【讨论】:

    • 谢谢你,弗拉德米尔。给我带来更多麻烦的示例实际上是具有抽象类型的示例。从概念上讲,我会说我在那里声明的接口是抽象的,但我从来没有必要这样声明它们。这使我得出结论,我真的不知道它们是什么。现在,关于过程指针和过程伪参数。我也没有为这些声明接口抽象。你能给我一个例子,如果我不声明一个抽象接口,它根本就行不通吗? (除了你已经说过编译器可以拒绝上面的那个。)
    • 你永远不需要抽象接口。您始终可以创建一个虚拟过程并在procedure() 中指向它。因此,抽象接口主要是为了您的方便和语言的完整性。
    • 我已经编辑了我的原始帖子。你能看看它,告诉我我的结论是否正确吗?另外,当您说“创建一个虚拟过程”时,您的意思是要声明某种将使用其显式接口的存根过程吗?再次感谢您。
    • 是的,我所说的虚拟程序是指一个无用但具有正确接口的存根。
    • @booNlatoT 关于您的编辑,没有任何改变,您仍然有错误,因为不存在 foo_sub 并且接口未声明为抽象的。请注意,编译器不需要诊断此类问题,只允许这样做。要更正您的代码,只需在单词 interface 之前添加单词 abstract
    猜你喜欢
    • 2011-04-07
    • 1970-01-01
    • 2012-03-18
    • 2016-11-24
    • 2014-01-20
    • 1970-01-01
    • 2023-03-03
    • 2011-04-04
    相关资源
    最近更新 更多