Как поддерживать несколько версий одного и того же производного типа в Fortran?

РЕДАКТИРОВАТЬ, чтобы предоставить более подробную информацию:

1) Код, предоставляющий библиотеки, нельзя (легко) изменить, поэтому profile_v1_type и profile_v2_type следует считать неизменяемыми.

Я реализовал предложение @francescalus, и оно работает для моего небольшого тестового примера, но я думаю, что недостаточно ясно понял проблему. Причина в том, что я могу изменять только свой код, а не код/типы, поступающие из библиотеки. Проблема будет заключаться в том, что оба будут иметь t в импортированном profile_type, который конфликтует с родительским типом.

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


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

(Очевидно) необходимо поддерживать оба во время выполнения, иначе я бы предварительно обработал все это во время компиляции.

На данный момент я лениво скопировал и вставил один и тот же код для каждой версии (и всех версий используемых производных типов) в отдельные подпрограммы (*_v1.f90, *_v2.f90).

Это раздражает и не очень ремонтопригодно.

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

Как я уже сказал выше, имена в основном одинаковы, например. (t, для температуры, скажем)

Из v1 библиотеки:

TYPE profile_v1_type
    REAL :: t
! loads of other stuff
END TYPE profile_v1_type

Из v2 библиотеки:

TYPE profile_v2_type
    REAL :: t
! loads of other stuff, more than the first version
END TYPE profile_v2_type

В моем коде:

TYPE profile_container_type
    TYPE(profile_v1_type) :: profile_v1
    TYPE(profile_v2_type) :: profile_v2
! other arrays that are common inputs to both
END TYPE

! It's actually USE'd then allocated and initialised elsewhere, but I hope you get the idea

!USE profile_container_mod, ONLY : profile_container

TYPE(profile_container_type), TARGET :: profile_container
TYPE(*) :: p

REAL :: t1

!Version determined by a namelist

IF (Version == 1) THEN
    p => profile_container % profile_v1
ELSE IF (Version == 2) THEN
    p => profile_container % profile_v2
ENDIF

t1 = p % t + 1
.
.
.

ifort 19 выдает эти (ожидаемые) ошибки:

test.f90(24): error #8776: An assumed type object must be a DUMMY argument.   [P]
TYPE(*), POINTER :: p
--------------------^
test.f90(24): error #8772: An assumed type object must not have the ALLOCATABLE, CODIMENSION, POINTER, INTENT(OUT) or VALUE attribute.   [P]
TYPE(*), POINTER :: p
--------------------^
test.f90(39): error #6460: This is not a field name that is defined in the encompassing structure.   [T]
t1 = p % t + 1
---------^
compilation aborted for test.f90 (code 1)

заменить TYPE(*) на CLASS(*) дает (все еще ожидается):

test2.f90(39): error #6460: This is not a field name that is defined in the encompassing structure.   [T]

t1 = p % t + 1 ! or some clever function...
---------^
compilation aborted for test2.f90 (code 1)

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

Я открыт для использования указателей C, если ответчик может предоставить простой пример для подражания. Я пытался (не недавно) решить эту проблему, используя совместимость C, но, очевидно, безуспешно!


person Biggestseagull    schedule 05.08.2019    source источник
comment
Вы знакомы с расширением типа? Очевидный подход состоит в том, чтобы иметь базовый тип с объединением двух типов, а затем иметь полиморфный указатель с этим базовым типом в качестве объявленного типа. Возможны ли такие изменения в вашем коде?   -  person francescalus    schedule 05.08.2019
comment
Возможно, стоит задать новый вопрос с дополнительными ограничениями: у многих людей есть библиотечный код, который они не могут изменить, и многие могут захотеть сделать подобное. Эти ограничения имеют большое значение.   -  person francescalus    schedule 09.08.2019


Ответы (1)


Неограниченные полиморфные сущности здесь не подходят.

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

type profile_base_type
  real t
end type

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

type, extends(profile_base_type) :: profile_v1_type
  ! v1 specific parts
end type

type, extends(profile_base_type) :: profile_v2_type
  ! v2 specific parts
end type

Затем мы можем объявить полиморфный указатель базового типа

class(profile_base_type), pointer :: p

который может указывать на цели любого из расширяющихся типов:

p => profile_container%profile_v1
p => profile_container%profile_v2

Теперь мы можем получить доступ к компонентам p типа profile_base_type.

t1 = p%t + 1

без использования конструкции типа select.

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

person francescalus    schedule 05.08.2019
comment
Спасибо @francescalus - я бы никогда не подумал об этом, но из вашего объяснения мне кажется, что каждая копия «t» в расширении переопределяет копию в базовом классе? Предположительно, я мог бы установить 't' и в базовом классе, если бы у меня было желание сделать это (у меня нет). Это очень похоже на то, как классы реализованы в C++ (с которым я только начинаю разбираться). Значит ли это, что с наследованием обращаются так же? - person Biggestseagull; 07.08.2019
comment
Я хотел сказать, что приму это решение, как только быстро проверю его в своем тестовом примере. Тогда я надеюсь, что правильно перевел свою большую проблему, так что ваше решение все еще работает для нее! - person Biggestseagull; 07.08.2019
comment
Это общая идея, но, возможно, лучше думать не о переопределении копии t, а о наследовании. Каждый расширяющий класс имеет компоненты базового класса. Переопределение было бы более подходящим в случае процедур с привязкой к типу, когда привязка каждого расширяющего типа указывает на другую процедуру. - person francescalus; 07.08.2019