Понимание FORTRAN расширяет типы и отменяет

Уважаемые пользователи и разработчики FORTRAN,

Я пытаюсь понять объектно-ориентированные концепции в стандартах FORTRAN 2003 (или позже). У меня есть некоторые знания на С++, поэтому я думаю, что между этими двумя языками существуют некоторые общие идеи, которые могут помочь мне лучше понять их. Пожалуйста, помогите мне немного разъяснить, так как я не нашел слишком много полезных объяснений в Интернете.

В С++ полиморфизм выполняется посредством деривации класса и переопределения функции-члена. Один определяет "абстрактный" базовый класс, где определены почти все виртуальные функции. Различные производные классы содержат фактическую их реализацию. Поэтому другие функции просто нужно программировать на основе "абстрактного" класса. Затем они работают для всех производных классов.

Я думаю, что в FORTRAN OOP выполняется аналогичным образом, но есть некоторые отличия. На мой взгляд, необходимо определить базовый тип с некоторыми виртуальными функциями, как С++. И другие функции/подпрограммы должны следовать определению функции-члена в базовом типе. То, что способ разрешения повторного использования функции/подпрограмм для всех расширяет типы.

У меня нет лучшей идеи о том, как программировать идею. Вот моя первая попытка:

type Basis
    integer                                 :: NBasis
contains
    private
    procedure                               :: DoNothing
    generic, public                         :: Constructor => DoNothing
    generic, public                         :: AllocateBasis => DoNothing
endtype Basis

type, extends(Basis) :: GridBasis
    private
    integer                                 :: NGrid
contains
    private
    procedure                               :: ConstructorGrid1
    procedure                               :: ConstructorGrid2
    generic, public                         :: Constructor => ConstructorGrid1, ConstructorGrid2, ConstructorGrid3
    procedure                               :: AllocateGridReal
    procedure                               :: AllocateGridCplx
    generic, public                         :: AllocateBasis => AllocateGridReal, AllocateGridCplx
endtype GridBasis
  • Во-первых, как я могу определить "AllocateBasis" в типе Basis, чтобы он работал как "виртуальная функция", и все расширенные типы должны определять свою собственную версию "AllocateBasis" ?

  • Во-вторых, как я могу определить "AllocateBasis" в типе GridBasis? Здесь определение содержит реальную реализацию.

  • В-третьих, как я могу сделать "AllocateBasis" в виде GridBasis функцией перегрузки? то есть есть реальная версия и сложная версия, и оба они называются "AllocateBasis" с реальными или сложными массивами ввода-вывода.

  • В-четвертых, NOPASS против PASS. Насколько я понимаю, если PASS установлен, то есть явный указатель на объект. Но когда NOPASS установлен, такого не происходит. Таким образом, PASS упрощается для разъяснения?

Я ценю любые комментарии.

Edit1:

Edit1 Удалено, поскольку это не имеет никакого отношения к вопросу.

Ответ 1

Сначала несколько комментариев/ответов на ваши вопросы:

  • Вы можете объявить процедуру привязки типа deferred. Затем вы должны определить свою подпись (интерфейс) только без конкретной реализации. Тип, содержащий процедуру deferred, должен быть объявлен abstract. Такие типы не могут быть созданы. Все расширяющиеся типы должны обеспечивать реализацию для данной процедуры, если они сами не являются abstract.

  • Чтобы обеспечить реализацию для процедуры deferred в расширяющемся типе, вы просто объявляете процедуру в расширяющемся типе и предоставляете реализацию для нее.

  • Вы не можете превратить общедоступную процедуру заданного типа в generic в расширяющемся типе. Однако вы можете определить generic уже в базовом типе и расширить его в своих производных типах.

  • Атрибут pass устанавливается по умолчанию, так что первым аргументом процедуры будет экземпляр типа. Вы можете, однако, указать его, чтобы сделать его более явным. Кроме того, вы можете использовать его в форме PASS(ARGNAME) для указания, какой аргумент (ARGNAME) должен быть экземпляром. Этот аргумент не должен быть первым в процедуре.

Ниже вы найдете пример с самосохранением, который должен содержать все функции, которые вы запросили:

module basis_module
  implicit none

  type, abstract :: Basis
    integer :: NBasis
  contains
    procedure(allocBasisR1Interface), deferred :: allocateBasisR1
    generic :: allocateBasis => allocateBasisR1
  end type Basis

  interface 
    ! Interface for real basis allocation
    subroutine allocBasisR1Interface(self, array)
      import
      class(Basis), intent(inout) :: self
      real, intent(in) :: array(:)
    end subroutine allocBasisR1Interface
  end interface

end module basis_module


module extension_module
  use basis_module
  implicit none

  type, extends(Basis) :: GridBasis
  contains
    ! Extending the mapping allocateBasis => allocateBasisR1 of
    ! the parent type.
    generic :: allocateBasis => allocateBasisC1
    procedure :: allocateBasisC1
    ! Implementation for the deferred procedure in Basis
    procedure :: allocateBasisR1
  end type GridBasis

contains

  subroutine allocateBasisR1(self, array)
    class(GridBasis), intent(inout) :: self
    real, intent(in) :: array(:)

    self%NBasis = size(array)
    print *, "GridBasis:allocateBasisR1"

  end subroutine allocateBasisR1


  subroutine allocateBasisC1(self, array)
    class(GridBasis), intent(inout) :: self
    complex, intent(in) :: array(:)

    self%NBasis = size(array)
    print *, "GridBasis:allocateBasisC1"

  end subroutine allocateBasisC1

end module extension_module


program test
  use extension_module
  implicit none

  type(GridBasis) :: mybasis
  real :: myRealArray(10)
  complex :: myComplexArray(5)

  call mybasis%allocateBasis(myRealArray)
  call mybasis%allocateBasis(myComplexArray)

end program test