Копирование подмножеств полиморфных массивов в целевые полиморфные массивы

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

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 (тип A) (заданные как 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 указывает на то, что происходит что-то странное.

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

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

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


person alekepd    schedule 23.06.2016    source источник
comment
Даже если есть способы исправить представленные исходные решения, было бы полезно обсудить, как эффективно перегрузить операторы присваивания в этом контексте.   -  person alekepd    schedule 23.06.2016
comment
В вашей select type попытке жалоба связана с тем, что model2(i) невозможно выделить. Действительно, вы не ссылаетесь на POBJECT в этой конструкции (которая связана с model1). Вы, вероятно, захотите попробовать еще select type на (распределенном) model2 и связанную с ним вещь в качестве цели назначения. [Или что-то вроде allocate(model2, mold=model1(indices)); select type (obj=>model2)...]   -  person francescalus    schedule 23.06.2016
comment
Что касается вашей попытки с выделением из источника, возможно, стоит отметить, что существуют проблемы с распределением из источника в gfortran. Хотя результат отличается, возможно, стоит явно указать границы в распределении. Что-то вроде allocate(model2(SIZE(indices)),source=model1(indices)).   -  person francescalus    schedule 23.06.2016
comment
Спасибо, что поймали это. Ошибка, кажется, сохраняется, поскольку я не выбрал модель2. Позже я попытался использовать вложенные операторы выбора типа, которые работают в подобном примере, хотя легко становятся уродливыми.   -  person alekepd    schedule 24.06.2016


Ответы (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.

person IanH    schedule 23.06.2016
comment
Да, похоже, это работает и удовлетворяет поставленный вопрос. В итоге я перегрузил оператор присваивания: в базовом классе у меня есть нулевой оператор присваивания, который разрешается только в том случае, если дополнительные методы, расширяющие интерфейс присваивания в дочернем классе, не захвачены. - person alekepd; 24.06.2016
comment
Это позволяет мне использовать обычный синтаксис присваивания, поскольку компилятор знает, что встроенное присваивание не будет вызываться ни в каком контексте. - person alekepd; 24.06.2016
comment
В расширении интерфейса присваивания мне все же пришлось удалить полиморфный контекст с операторами выбора типа. - person alekepd; 24.06.2016
comment
Почему вы использовали оператор блока и move_alloc для определения модели 1 здесь? - person alekepd; 24.06.2016
comment
В отсутствие поддержки компилятором присваивания полиморфному размещаемому объекту определение неполиморфного размещаемого объекта и последующее перемещение его на место является относительно ясным, кратким и эффективным способом определения полиморфного объекта. Мне не нужен временный неполиморфный объект в другом месте, поэтому область его идентификатора ограничена BLOCK. - person IanH; 25.06.2016
comment
Помните, что определенное назначение вводит некоторые ограничения, которых нет во внутреннем назначении, связанные с невозможностью устранения неоднозначности определенных процедур на основе атрибута ALLOCATABLE. Я не знаю, что вы подразумеваете под нулевым присваиванием. - person IanH; 25.06.2016
comment
Ах. Я использовал allocate(linearDivisionModel::model(n)) вместо передачи. Под нулевым присваиванием я подразумеваю, что, хотя я формально определил, как работает общее присваивание модели с использованием метода, эта процедура присваивания только помечает объекты как сломанные и не копирует данные, состояние, которое уже проверяется. Поскольку общий интерфейс имеет как этот метод, так и другой метод для любых конкретных подклассов, который я добавляю, который фактически назначает объекты (например, linearModel для linearModel), выполняются действительные назначения, а неправильные перехватываются во время выполнения. - person alekepd; 25.06.2016