Каков канонический способ размещения и конструирования полиморфных объектов в Фортране? - PullRequest
1 голос
/ 07 ноября 2019

Я хочу создать массив полиморфных объектов, у которых есть конструкторы, принимающие разные фиктивные аргументы в зависимости от их динамического типа. Прочитав о пользовательских и структурных конструкторах, я не вижу способа применить эти концепции к динамически размещаемым объектам. Имея опыт работы в C ++, я привык к тому, что я могу использовать одну и ту же конструкторскую «функцию-член» при распределении объектов динамически или в стеке, но как я могу явно вызывать определяемые пользователем конструкторы Fortran для выделенных объектов?

Вместо этого я попытался поиграться с универсальными и привязанными к типу функциями инициализации:

module mod
type :: basis_t
contains
    procedure, public :: init_func => init_base
    ! I want a generic constructor function
    generic, public   :: init => init_func 
end type

type, extends(basis_t) :: extended_t
contains
    ! cannot work, init_extended has a different signature from init_base
    procedure, public :: init => init_extended 
end type

type wrapper_t
   type(basis_t), pointer :: obj
end type

contains
   subroutine init_base(this)
      class(base_t), intent(inout) :: this
   end subroutine

   subroutine init_extended(this, param)
      class(extended_t), intent(inout) :: this
      integer :: param
   end subroutine
end module

program
   use mod
   implicit none

   type(wrapper_t) :: arr(2)
   allocate(basis_t::arr(1)%obj)
   allocate(extended_t::arr(2)%obj)
   call arr(1)%obj%init    ! calls init_basis
   call arr(2)%obj%init(4) ! calls init_extended
end program

Но я не верю, что я на правильном пути, как, например, в C ++, я былучше, например, сделать

basis_t* arr[2];
arr[0] = new basis_t;
arr[1] = new extended_t{ 4 };

Важным отличием является то, что конструкторы в C ++ не привязаны к типу / виртуальны, как в моем подходе на Фортране. Что я могу сделать?

1 Ответ

2 голосов
/ 08 ноября 2019

Роль конструктора в Фортране может быть предоставлена:

  • Предоставленный язык Конструктор структуры .

  • Функция с результатом, который соответствует типу создаваемого объекта. Язык позволяет универсальной функции иметь то же имя, что и производный тип, а также позволяет ссылке на такую ​​функцию перегружать то, что в противном случае было бы ссылкой на конструктор структуры для типа.

  • Подпрограмма, которая определяет аргумент намерения (out) соответствующего типа.

То, что вы используете, зависит от обстоятельств и личных предпочтений в определенной степени. Предоставленный языком конструктор структуры в некоторых случаях может использоваться в константных выражениях, но допускает только простое определение значений компонентов (без исполняемого кода);форма ссылки на функцию позволяет вам выполнять произвольный код как часть конструкции объекта, не может использоваться в константных выражениях, не может легко указывать на сбой конструкции и может быть дорогостоящим (в зависимости от деталей реализации процессора на Фортране), если построенный объект большой;форма подпрограммы требует отдельного оператора вызова (конструктор не может быть частью большего выражения) и не может использовать преимущества универсальной функции языка перегрузки имени / структуры.

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

Указатели в Фортране в основном используются для ссылочной семантики (потому что они являются ссылками). Как правило, вы не хотите использовать их, если хотите использовать семантику значений - используйте allocatables.

TYPE :: ta
  INTEGER :: a
END TYPE ta

TYPE, EXTENDS(ta) :: tb
  REAL :: b
END TYPE :: tb

INTERFACE tb
  PROCEDURE :: tb_construct
END INTERFACE tb

TYPE, EXTENDS(ta) :: tc
END TYPE tc

TYPE :: ta_item
  CLASS(ta), ALLOCATABLE :: item
END TYPE ta_item

!...

FUNCTION tb_construct(arg)
  INTEGER, INTENT(IN) :: arg
  TYPE(tb) :: tb_construct
  tb_construct%a = arg + 1
  tb_construct%b = arg / 2.0
END FUNCTION tb_construct

SUBROUTINE ConstructTC(obj, arg, stat)
  CLASS(ta), INTENT(OUT), ALLOCATABLE :: obj
  INTEGER, INTENT(IN) :: arg
  INTEGER, INTENT(OUT) :: stat
  TYPE(tc), ALLOCATABLE :: tmp
  IF (arg < 0) THEN
    ! Construction failed.
    stat = 1
    RETURN
  END IF
  tmp%a = arg + 4
  CALL MOVE_ALLOC(tmp, obj)
  stat = 0    ! Construction succeeded.
END SUBROUTINE ConstructTC

!...

TYPE(ta_item) :: the_items(3)
INTEGER :: stat

! Structure constructor
the_items(1)%item = ta(1)

! Overloaded function.
the_items(2)%item = tb(2)

! Subroutine.
CALL ConstructTC(the_items(3)%item, 3, stat)
IF (stat /= 0) ERROR STOP 'It failed.'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...