【问题标题】:How to support multiple versions of the 'same' derived type in Fortran?如何在 Fortran 中支持多个版本的“相同”派生类型?
【发布时间】:2019-08-05 15:41:03
【问题描述】:

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

1) 提供库的代码不能(轻松)更改,因此应假定 profile_v1_type 和 profile_v2_type 是不可变的。

我已经实施了@francescalus 建议,它适用于我的小测试用例,但我认为我对这个问题还不够清楚。原因是我只能修改我的代码而不是来自库的代码/类型。问题是两者都会在导入的 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)

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

【问题讨论】:

  • 您熟悉类型扩展吗?一个明显的方法是使用两种类型的联合的基类型,然后使用该基类型作为声明类型的多态指针。您的代码中是否可以进行此类更改?
  • 有额外限制的新问题可能值得提出:很多人拥有无法更改的库代码,而且许多人可能想做类似的事情。这些限制有很大的影响。

标签: fortran derived-types


【解决方案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

无需使用选择类型构造。

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

【讨论】:

  • 谢谢@francescalus - 我永远不会想到这一点,但从你的解释来看,在我看来,扩展中的每个“t”副本都会覆盖基类中的副本?如果我想这样做(我不这样做),大概我也可以在基类中设置“t”。这看起来与 C++ 中类的实现方式非常相似(我刚刚开始掌握)。那么继承的处理方式是否相同?
  • 我的意思是说,一旦我在我的测试用例中快速尝试过这个解决方案,我就会接受它。那么我希望我正确地翻译了我的大问题,这样你的解决方案仍然适用!
  • 这是一般的想法,但最好不要考虑“覆盖”“t”的副本,而是“继承”。每个扩展类都有基类的组件。 “覆盖”更适合类型绑定过程的情况,其中每个扩展类型的绑定都指向不同的过程。
猜你喜欢
  • 2012-10-23
  • 2019-03-02
  • 2018-04-08
  • 2021-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-07
相关资源
最近更新 更多