如何在 Fortran 中支持多个版本的 'same' 派生类型?

How to support multiple versions of the 'same' derived type in Fortran?

编辑以提供更多详细信息:

1) 无法(轻易)更改提供库的代码,因此应假定 profile_v1_typeprofile_v2_type 是不可变的。

我已经实现了@francescalus 的建议,它适用于我的小测试用例,但我认为我对这个问题还不够清楚。原因是我只能修改我的代码,而不能修改来自库的 code/types。问题是两者都将在导入的 profile_type 中包含 t,这与父类型发生冲突。

但我要实现一些东西,在其中复制我想要的派生类型的内容,然后使用指针和类型绑定过程指向我想要的 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)

用 CLASS(*) 替换 TYPE(*) 给出(仍然是预期的):

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 互操作性来解决这个问题,但显然没有成功!

无限多态实体不是这里的正确方法。

相反,我们可以定义一个基本类型,它包含所有公共数据和处理各种其他类型。在这里,我们称这个基类型为 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

现在,我们可以访问类型为 profile_base_type

p 的组件
t1 = p%t + 1

无需使用 select 类型结构。

当然,不能以这种方式访问​​扩展类型的那些特定方面,但还有其他考虑因素。