Построение производных и расширенных типов

Я новичок в Fortran OOP и сталкиваюсь с некоторыми проблемами при инициализации родительских и производных типов. У меня есть один модуль, содержащий родительский тип object (извините за чрезмерное употребление слова...) и производный от него тип circle, который имеет дополнительное поле radius.

Способ, которым мне нужно инициализировать тип object, теперь требует использования фиктивного аргумента для радиуса, которого я хотел бы избежать. Итак, на данный момент то, что у меня есть, работает, но я хотел бы знать более причудливый способ сделать это, поскольку это не кажется очень практичным, если мне понадобятся дополнительные производные типы от object в будущем.

Я предполагаю, что использование object в качестве абстрактного родительского типа помогло бы в этом смысле? Или используя общие процедуры, но я действительно не знаю, как это сделать.

Код ниже.

module objectMod
  implicit none

  type :: object
     real,allocatable   :: x(:,:)           ! position vector (points) --- (M,{i,j})
     real               :: centre(2)        ! centre of the object
     integer            :: M=50             ! number of Lagrangian points of the object (default)
     real               :: eps=0.1          ! kernel of the surface (default)
   contains
     procedure :: init=>init_object
  end type object

contains

  subroutine init_object(a,centre,radius,M,eps)
    implicit none

    class(object),intent(inout)   :: a
    real,intent(in)               :: centre(2)
    integer,intent(in),optional   :: M
    real,intent(in),optional      :: eps
    real,intent(in),optional      :: radius ! ignored for object

    if(present(M)) a%M = M
    if(.not.allocated(a%x)) allocate(a%x(a%M,2))
    a%centre = centre
    if(present(eps)) a%eps = eps
  end subroutine init_object
end module objectMod

module geomMod
  use objectMod
  implicit none
  real,parameter :: PI = 3.14159265

  type,extends(object) :: circle
     real              :: radius    ! radius
   contains
     procedure :: init=>init_circle
  end type circle

contains

  subroutine init_circle(a,centre,radius,M,eps)
    implicit none
    class(circle),intent(inout) :: a
    real,intent(in)             :: centre(2)
    real,intent(in),optional    :: radius
    integer,intent(in),optional :: M
    real,intent(in),optional    :: eps

    integer :: i
    real    :: dtheta

    ! object type attributes initialization
    a%centre = centre
    if(present(M)) a%M = M
    if(.not.allocated(a%x)) allocate(a%x(a%M,2))
    if(present(eps)) a%eps = eps
    ! circle type attributes initialization
    a%radius = radius

    dtheta = 2.*PI/real(a%M-1)
    do i = 1,a%M
      a%x(i,1) = a%radius*cos(dtheta*(i-1))+a%centre(1)
      a%x(i,2) = a%radius*sin(dtheta*(i-1))+a%centre(2)
    end do
  end subroutine init_circle
end module geomMod

person b-fg    schedule 16.03.2016    source источник
comment
Я не могу отбросить радиус, потому что тогда я получаю ошибку несоответствия аргументов (очевидно, я пробовал это). Под причудливым я подразумеваю способ не нуждаться в фиктивных аргументах для родительского конструктора, поскольку он будет становиться неприятным, когда я добавляю все больше и больше расширенных типов объектов. И иногда мне нужно будет создать экземпляр объекта.   -  person b-fg    schedule 16.03.2016
comment
Я фактически использовал этот источник для кодирования этого. Поиск там игнорирует форму, поскольку это часть кода, которую я фактически использую и которая требует использования фиктивных аргументов для родительских производных типов.   -  person b-fg    schedule 16.03.2016


Ответы (1)


Ошибка, которую вы делаете, заключается в том, что вы создаете конструкторы как процедуры с привязкой к типу. Требование соответствия списка аргументов против вас.

Процедуры с привязкой к типу просто не подходят для конструкторов (или инициализаторов).

В Fortran мы используем функции, возвращающие экземпляр объекта для его инициализации.

function init_object(centre,M,eps) result(a)
  type(object) :: a
  real,intent(in)               :: centre(2)
  integer,intent(in),optional   :: M
  real,intent(in),optional      :: eps
  !NO radius
end function init_object


interface object
  procedure init_object
end interface



....



obj = object(my_centre, my_m, my_eps)

Точно так же вызываются конструкторы структур по умолчанию.


Я вижу, вы взяли пример из какого-то онлайн-учебника. Я считаю, что пример плохой дизайн. Если вы не согласны, вам придется жить с этими проблемами, которые он приносит.


Итак, в основном вам нужен метод инициализации вместо конструктора (см. Зачем использовать метод инициализации вместо конструктора? для обсуждения. В Objective-C используется нечто подобное https://www.binpress.com/tutorial/objectivec-lesson-11-object-initialization/76, но в более позднем Swift вместо этого используются конструкторы.).

Вы можете использовать дженерики следующим образом:

    module types

        type t1
          integer :: i
        contains
          generic :: init => init_t1
          procedure, private :: init_t1
        end type

        type, extends(t1) :: t2
          integer :: j
        contains
          generic :: init => init_t2
          procedure, private :: init_t2
        end type

        type, extends(t2) :: t3
          integer :: k
        contains
          generic :: init => init_t3
          procedure, private :: init_t3
        end type

    contains


      subroutine init_t1(self, i)
        class(t1) :: self
      end subroutine

      subroutine init_t2(self, i, j)
        class(t2) :: self
      end subroutine

      subroutine init_t3(self, i, j, k)
        class(t3) :: self
      end subroutine

    end module

На мой взгляд, это просто уродливо и не по-фортрански, но делает то, что вы хотите.

Вам придется позаботиться о том, чтобы пользователь случайно не вызвал неправильную версию, перегрузив ее чем-то, что выдаст сообщение об ошибке.

Мой совет по-прежнему заключается в том, чтобы скорее следовать общепринятым шаблонам, чем изобретать свои собственные способы, которые могут сбить с толку тех, кто привык к мейнстриму.

person Vladimir F    schedule 16.03.2016
comment
Спасибо за ваш ответ @Vladimir-F. Я хотел бы, чтобы они были процедурами с привязкой к типу, поэтому я думаю, что лучше всего было бы установить «объект» в качестве абстрактного родительского типа. Но тогда я не смогу использовать сам объект в основной программе, верно? - person b-fg; 16.03.2016
comment
Я не вижу причин для абстрактного типа. И определенно нет причин, которые могли бы иметь какое-либо отношение к моему ответу. Пожалуйста, прочтите его еще раз Если вы все равно не хотите использовать процедуры с привязкой к типу, вы можете, но у вас не может быть одного универсального %init(), вы должны использовать разные имена для каждого типа и, возможно, вы должны сделать их непереопределяемый. - person Vladimir F; 16.03.2016
comment
Кстати, могу ли я спросить, почему они являются процедурами с привязкой к типу? Я не могу найти. Помните, что в Java или C++ или подобных людях также используются функции, которые возвращают объект (или ссылку на него). - person Vladimir F; 16.03.2016
comment
Я прочитал и понял ваш ответ. Я мог бы сделать это (использовать разные имена для разных типов), но я хочу использовать одну и ту же процедуру init для каждого object или расширенного типа, такого как circle, поскольку с точки зрения пользователя (людей, которые будут настраивать основной файл) лучше иметь одно имя процедуры для инициализации любых object или производных типов. - person b-fg; 16.03.2016
comment
Да, я бы хотел инициализатор вместо конструктора. Я попробую, но согласен, что это выглядит немного странно. В любом случае, я думаю, что использование абстрактного родителя упростит задачу. - person b-fg; 17.03.2016