2016-03-17 7 views
2

Я пытаюсь настроить код Оранга Fortran, где родительский тип geom имеет поле с возможностью выделения shape. Это поле выделяется одним из расширенных типов geom, которые являются circle или rectangle. В другом модуле у меня есть тип body, который содержит поле geom среди других.Полиморфический родительский тип типа

Так что я хочу иметь тип geom, который может фактически получить доступ к различным типам (тогда будет доступ к различным полям в зависимости от типа) и тип body, который инициализируется геометрией.

Найти код. Это модуль для геометрии:

module geomMod 

    implicit none 

    type :: geom 
    class(*),allocatable :: shape 
    contains 
    procedure,private  :: set_geom 
    generic    :: assignment(=) => set_geom 
    end type geom 

    type,extends(geom) :: circle 
    integer :: id=1 
    real :: centre(2) 
    real :: radius 
    end type circle 

    type,extends(geom) :: rectangle 
    integer :: id=2 
    real :: centre(2) 
    real :: length(2) 
    end type rectangle 

contains 

    subroutine set_geom(a,b) 
    implicit none 
    class(geom),intent(inout) :: a 
    class(*),intent(in) :: b 

    allocate(a%shape,source=b) 
    end subroutine set_geom 

end module geomMod 

Это модуль для тела:

module bodyMod 
    use geomMod 
    implicit none 

    type :: body 
    class(geom),allocatable :: geom1 
    real,allocatable  :: x(:,:)   
    integer     :: M=50    
    real     :: eps=0.1   
    contains 
    procedure :: init 
    end type body 

contains 

    subroutine init(a,geom1,M,eps) 
    implicit none 

    class(body),intent(inout) :: a 
    class(geom),intent(in)  :: geom1 
    integer,intent(in),optional :: M 
    real,intent(in),optional :: eps 

    allocate(a%geom1,source=geom1) 

    if(present(M)) a%M = M 
    if(present(eps)) a%eps = eps 
    if(.not.allocated(a%x)) allocate(a%x(a%M,2)) 
    end subroutine init 

end module bodyMod 

И это, как я инициализировать их из основного файла:

use bodyMod 
    implicit none 

    integer,parameter :: M = 500 
    real,parameter :: eps = 5 

    type(body) :: b 
    type(geom) :: geom1 

    geom1 = circle(centre=(/1,1/),radius=0.5) 

    call b%init(geom1=geom1,M=M,eps=eps) 

Однако Я получаю эту компиляцию с gfortran 4.8.4.

geom1 = circle(centre=(/1,1/),radius=0.5) 
      1 
Error: No initializer for component 'shape' given in the structure constructor at (1)! 
+0

gfortran 4.8.4. С этим тегом я просто имею в виду, что я кодирую стиль F2003 OOP вместо F90. Но я удаляю его, так как это может ввести в заблуждение. –

+0

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

+0

Не проще ли изменить «geom» как пустой базовый класс и просто выполнить «вызов b% init (circle (center = [1.0,1.0], radius = 0.5)), M = M, eps = eps) "? (потому что «тело» уже имеет «класс (geom), allocatable», который кажется достаточным для хранения информации геометрии.) – roygvib

ответ

1

Ваша структура конструктор circle ссылается со значениями всего за две компоненты указаны

geom1 = circle(centre=(/1,1/),radius=0.5) 

и ваш компилятор не нравится.

Тип circle состоит из четырех компонентов, shape, id, centre и radius. В ссылке на конструктор структуры не всегда необходимо давать значения для всех компонентов, и здесь применимы два случая.

Первый случай, когда компонент имеет инициализацию по умолчанию. Вы также имеете право, как и вы, опустить значение для компонента id.

Второй случай - для выделяемого компонента. Здесь ситуация становится проблематичной: правила изменились между стандартами Fortran 2003 и Fortran 2008.

В соответствии с Fortran 2008 разрешено опускать значение, соответствующее распределяемому компоненту. В Fortran 2003 это не так.

Ошибка вашего компилятора указывает на то, что он соответствует правилам Fortran 2003 в этом отношении и требует значения для выделяемого компонента shape. gfortran - such a compiler.

Чтобы обеспечить такое значение, в результате чего компонент нераспределенные, можно иметь

geom1 = circle(shape=NULL(),centre=(/1,1/),radius=0.5) 
+0

Привет, спасибо! Я этого не знал, и это полезно. Также я видел, что могу фактически объявить 'shape' как указатель' class (*), pointer :: shape => null() ', поэтому я могу его инициализировать. Тем не менее, я пытался получить доступ к 'radius', и я не могу этого сделать:' print *, b% geom1% radius' Я понимаю, что 'radius' не является членом структуры' geom'. Это не очевидно, но я ожидал, что смогу получить доступ к этому пути, так как я выделил 'shape' в' allocate (a% shape, source = b) 'теперь указывает на тип' circle', который содержит 'radius'. Как я мог это сделать? Еще раз спасибо. –

+0

С компонентом указателя, действительно, состояние указателя по умолчанию может быть не привязано. Тем не менее, я был бы осторожен, чтобы предпочесть компонент указателя над распределяемым компонентом, чтобы получить это преимущество. – francescalus

+0

И как я могу получить доступ к «радиусу»? –