2016-06-23 1 views
1

У меня есть два полиморфных массива производного типа (obj1 и obj2) в подпрограмме. Основываясь на использовании подпрограммы, в то время как типы двух массивов могут отличаться, оба массива одного типа; например, оба типа A или оба типа B. В приведенном ниже примере кода я показываю только один подтип абстрактного класса (модели), в то время как на самом деле я хочу, чтобы это работало по нескольким подтипам. Кроме того, в производственном коде элементы модели1 были изменены до этой копии.Копирование подмножеств полиморфных массивов в целевые полиморфные массивы

program test 

     use env_kindtypes,    only: si, dp 

     use abs_obj_model,    only: model 
     use obj_linearDivisionModel, only: linearDivisionModel 

     implicit none 

     class(model),allocatable  :: model1(:), model2(:) 

     allocate(linearDivisionModel::model1(10)) 

     !want e.g. model2 = model1([1,4,6,2]) 

     ![...] 

Учитывая obj1, obj2 (тип А) (с учетом как MODEL1, MODEL2 типа linearDivisionMode в примере кода) и набор индексов, я хочу перенести указанные элементы из obj1 в obj2, выделяя obj2 в процесс.

Я пробовал несколько подходов, чтобы сделать это, но никто не работает.

Во-первых, я пробовал прямое назначение с использованием векторного индекса; это не удается, жалуясь, что прямое присвоение распределяемого полиморфного массива еще не поддерживается.

indices = [ 1 , 2 ] 
model2 = model1(indices) 

результат:

  model2 = model1(indices) 
     1 
Error: Assignment to an allocatable polymorphic variable at (1) is not yet supported 

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

allocate(model2,source=model1(indices)) 

результат выполнения:

Operating system error: Cannot allocate memory 
Memory allocation failed 

Error termination. Backtrace: 
#0 0x434471 in __obj_lineardivisionmodel_MOD___copy_obj_lineardivisionmodel_Lineardivisionmode 
    at build/processed_src/obj_linear_model.f90:462 
#1 0x436c75 in cg_charge_fit 
    at build/processed_src/test.f90:37 
#2 0x403019 in main 
    at build/processed_src/test.f90:22 

Работает, но не достаточно для моих целей.

allocate(model2,source=model1(1:2)) 

В-третьих, я был в состоянии выделить полиморфный массив в надежде вручную перенося подэлементов: Тем не менее, когда я пытаюсь сделать это, я получаю жалобы полиморфных объектов и внутренней уступки, которые я возвращаюсь позже в этом посте.

indices = [ 1 , 2 ] 
allocate(model2(size(indices)),source=model1(1)) 
do i=1,size(indices) 
     model2(i) = model1(indices(i)) 
enddo 

Error: Nonallocatable variable must not be polymorphic in intrinsic assignment at (1) - check that there is a matching specific subroutine for '=' operator. 

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

select type (POBJECT => model1) 
     TYPE IS (linearDivisionModel) 
      allocate(linearDivisionModel::model2(size(indices))) 
      do i=1,size(indices) 
         model2(i) = POBJECT(indices(i)) 
      enddo 
end select 

результаты:

model2(i) = model1(indices(i)) 
1 
Error: Nonallocatable variable must not be polymorphic in intrinsic assignment at (1) - check that there is a matching specific subroutine for '=' operator 

В работе вокруг, я надеялся использовать промежуточный объект указателя, и источник выделения из этого. Из-за стандарта f2008 (который применяется здесь) я не могу назначить указатель на векторный индексированный массив. Интересно, что если я создаю указатель, векторный указатель, указатель, компилятор segfaults, указывая на то, что происходит что-то странное.

Чтобы обратиться к жалобам компилятора о внутреннем назначении, я рассмотрел возможность написания подпрограмм; однако это вызывает новый набор забот: родительский тип, который оба эти наследования наследует, является абстрактным, и я не могу указать общий оператор назначения в этом классе, что приводит к сложному родительскому классу, который требует довольно много частные методы для копирования, поскольку он не указывает никаких частных переменных. Кроме того, преобразование между подклассами A и B плохо определено.Это все еще кажется единственным оставшимся выходом, и кажется сложным.

Как я могу эффективно передать указанные полиморфные подмассивы?

Я использую версию gfortran 6.1.1.

+0

Даже если есть способы исправить представленные исходные решения, было бы полезно, если бы некоторые обсуждения о том, как эффективно перегрузить операторы присваивания в этом контексте. – alekepd

+0

В вашей попытке 'select type' жалоба касается' model2 (i) 'нераспределяемой. В самом деле, вы не ссылаетесь на 'POBJECT' в этой конструкции (которая связана с' model1'). Вероятно, вы захотите попробовать еще один 'select type' на (выделенной)' model2' и связанную с ним роль в качестве целевой цели. [Или что-то вроде 'allocate (model2, mold = model1 (индексы)); select type (obj => model2) ... '] – francescalus

+0

Для вашей попытки выделения ресурсов вы можете заметить, что есть [проблемы] (http://stackoverflow.com/q/34384145) с распределением ресурсов gfortran. Хотя результат отличается, может быть, стоит явно указать границы распределения. Что-то вроде 'allocate (model2 (SIZE (индексы)), source = model1 (индексы)). – francescalus

ответ

1

С полной поддержкой F2008 это просто оператор присваивания.

В рамках ограничений этого компилятора вам может потребоваться рассмотреть вложенные конструкции SELECT TYPE, которые устраняют полиморфную природу левой и правой сторон задания.

module my_types 
    implicit none 

    type, abstract :: model 
    end type 

    type, extends(model) :: linearDivisionModel 
    character :: comp 
    end type linearDivisionModel 
end module my_types 

program p 
    use my_types 
    implicit none 

    class(model),allocatable  :: model1(:), model2(:) 
    integer, allocatable :: indicies(:) 

    ! define model1. 
    block 
    type(linearDivisionModel), allocatable :: tmp(:) 
    allocate(tmp(10)) 
    tmp%comp = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 
    call move_alloc(tmp, model1) 
    end block 

    indicies = [1, 2, 4, 6] 

    ! allocate model2. 
    allocate(model2(size(indicies)), mold=model1) 

    ! define model2 
    select type (model1) 
    type is (linearDivisionModel) 
    select type (model2) 
    type is (linearDivisionModel) 
     model2 = model1(indicies) 
    end select 
    end select 

    ! display results. 
    select type (model2) 
    type is (linearDivisionModel) 
    print *, model2%comp 
    end select 
end program p 

Вышеупомянутое работает, работает с текущим gfortran багажником.

+0

Да, это похоже на работу и удовлетворяет поставленному вопросу. Я закончил перегрузку оператора присваивания: в базовом классе у меня есть оператор нулевого присваивания, который разрешен только тогда, когда дополнительные методы, расширяющие интерфейс назначения в дочернем классе, не захватываются. – alekepd

+0

Это позволяет использовать обычный синтаксис присваивания, поскольку компилятор знает, что внутреннее назначение не будет вызываться в любом контексте. – alekepd

+0

В расширении интерфейса назначения мне все же пришлось удалить полиморфный контекст с помощью операторов типа select. – alekepd