4

在现代版本的 Fortran 中是否可以将 kind 参数传递给子程序并使用它来将变量“转换”为这种类型?例如,在以下代码中,我尝试在打印之前将默认整数转换为 16 位整数。

program mwe

! Could use iso_fortran_env definition of int16, but I am stuck with
! old versions of ifort and gfortran.
! use, intrinsic :: iso_fortran_env, only : int16

implicit none

! 16-bit (short) integer kind.
integer, parameter :: int16 = selected_int_kind(15)

call convert_print(123, int16)

contains

  subroutine convert_print(i, ikind)
    implicit none
    integer, intent(in) :: i
    integer, intent(in) :: ikind

    print*, int(i, ikind)

  end subroutine convert_print

end program mwe

使用此示例代码,英特尔 Fortran 编译器抱怨说

mwe.f(24): error #6238: 在这个上下文中需要一个整数常量表达式。[IKIND]
...
mwe.f(24): error #6683: kind 类型参数必须是编译时常量 [IKIND]

和 gfortran 抱怨

(1) 处固有的 'int' 的 'kind' 参数必须是常数

在这种情况下,使用print*, int(i, int16)代替print*, int(i, ikind)当然可以正常工作。但是,如果convert_print在未定义的模块中定义,int16那么这将是无用的。

有没有办法将 kind 参数作为常量传递给子程序?

4

3 回答 3

5

我也有同样的问题。我发现不允许将 kind 数据类型作为参数传递给过程非常不方便。就我而言,我正在编写一个子程序来从文件中读取矩阵并获取我想要的数据类型的对象。我必须编写四个不同的子程序:ReadMatrix_int8(…)、ReadMatrix_int16(…)、ReadMatrix_int32(…) 和 ReadMatrix_int64(…),它们只不过是相同的代码,只有一行不同:

integer(kind=xxxx), allocatable, intent(out) :: matrix(:,:)

只编写一个子例程并将 xxxx 作为参数传递是有意义的。如果我找到任何解决方案,我会告诉你。但是恐怕没有比编写四个子程序然后编写一个接口来创建一个通用程序更好的解决方案,例如:

interface ReadMatrix
     module procedure ReadMatrix_int8
     module procedure ReadMatrix_int16
     module procedure ReadMatrix_int32
     module procedure ReadMatrix_int64
end interface
于 2013-02-01T18:44:28.067 回答
3

据我所知,Fortran 2003 标准( PDF,4.5 MB )明确禁止我尝试做的事情:

5.1.2.10 参数属性

除非已在同一语句中定义、在先前语句中定义或通过使用或主机关联使其可访问,否则不得引用命名常量。

因此,我似乎需要为我希望进行的每个转换定义一个函数,例如:

subroutine print_byte(i)
  implicit none
  integer, intent(in) :: i

  print*, int(i, int8)

end subroutine print_byte

subroutine print_short(i)
  implicit none
  integer, intent(in) :: i

  print*, int(i, int16)

end subroutine print_short

subroutine print_long(i)
  implicit none
  integer, intent(in) :: i

  print*, int(i, int32)

end subroutine print_long

显然,以上所有内容都必须重载才能接受不同类型的输入参数。这似乎需要做很多工作才能解决无法传递常数的问题,所以如果有人有更好的解决方案,我很想看到它。

于 2012-11-06T15:07:56.057 回答
2

这位英特尔专家给出了解释和优雅的解决方案。我无法更好地解释它。完整引用如下

“有一天,当我在当地杂货店的过道上闲逛时,一位女士把我招到一张桌子旁,问我想不想‘尝尝进口巧克力? ... 吉拉德利?我问那个女人,加利福尼亚是否已经脱离联邦,因为吉拉德利,尽管它的意大利名字,来自旧金山。我想从新罕布什尔州的有利位置来看,加利福尼亚还不如成为另一个国家,很多正如著名的索尔·斯坦伯格 1976 年《纽约客》封面“第 9 大道的世界观”中所描绘的那样。

(有人警告我,我的博客没有足够的任意链接——这应该会保留一段时间。)

同样,在 Fortran 中(我敢打赌你想知道我什么时候能做到这一点),有些东西可能如此接近,但看起来却如此遥远。不久前,我正在为英特尔 Visual Fortran 编写一个新模块,为 Win32 进程状态 API 提供声明。这将包含类型和常量的声明以及 API 例程的接口块,其中一些接受新类型的参数。自然的倾向是这样写:

MODULE psapi
TYPE sometype
some component
END TYPE sometype

INTERFACE
FUNCTION newroutine (arg)
INTEGER :: newroutine
TYPE (sometype) :: arg
END FUNCTION newroutine
END INTERFACE

END MODULE psapi

如果你这样做并编译了它,你会得到一个错误,即类型 sometype 在 arg 的声明中未定义。“什么?不是未申报,在同一个模块上面我可以看到!” 嗯,是的,也不是。是的,它是在模块中声明的,并且可以在模块中的任何地方使用,除了.. 除了在接口块中!

问题是接口块是“外部例程的窗口”——假设例程是用 Fortran 编写的,它们基本上复制了您在实际外部例程中看到的声明。因此,它们不会“宿主关联”来自封闭范围单元(本例中的模块)的任何符号。

在 Fortran 90 和 Fortran 95 中,典型的解决方案是创建一个单独的模块,比如“psapi_types”,其中包含要使用的所有类型和常量,然后在每个函数中添加一个 USE 语句,就像您将不得不在用 Fortran 编写的假设外部例程中。(如果它是用 Fortran 编写的,Doctor 会用湿打孔卡拍打你的手腕,并告诉你把例程变成一个模块过程,然后你就不用担心这种胡说八道了。)所以你最终会得到一些东西像这样:

MODULE psapi
USE psapi_types ! This is for the benefit of users of module psapi
INTERFACE
FUNCTION newroutine (arg)
USE psapi_types
INTEGER :: newroutine
TYPE (sometype) :: arg
...

使用 Intel Visual Fortran 的人都知道,事实上有一个巨大的模块 IFWINTY 用于此目的,其中包含其他 Win32 API 的所有类型和常量。这是凌乱和不雅的,但这是你必须做的。到现在...

Fortran 2003 试图为这种令人遗憾的情况恢复一些优雅,但为了保持与旧源的兼容性,它不能仅仅声明接口块参与主机关联。相反,创建了一个新语句 IMPORT。IMPORT 只允许出现在接口块中,它告诉编译器导入主机范围内可见的名称。

IMPORT 放在任何 USE 语句之后,但在接口主体(FUNCTION 或 SUBROUTINE 声明)中的任何 IMPLICIT 语句之前。IMPORT 可以有一个可选的 import-name-list,很像 USE。没有一个,主机中可访问的所有实体在接口主体内都变得可见。使用列表,只有命名实体是可见的。

使用 IMPORT,我的 PSAPI 模块可以看起来像第一个示例,但有以下更改:

...
FUNCTION newroutine (arg)
IMPORT
INTEGER :: newroutine
TYPE(sometype) :: arg
...

如果我愿意,我可以使用:

IMPORT :: sometype

说我只想要一个进口的名字。漂亮整洁,集中在一个模块中!

“但你为什么要告诉我这个?”,您可能会问,“这是 Fortran 2003 的一个特性,而英特尔 Fortran 还没有完成 Fortran 2003 的所有功能。” 确实如此,但我们不断向编译器添加越来越多的 F2003 功能,并且 IMPORT 早在 8 月就实现了!因此,如果您保持合理的最新状态,您现在可以随心所欲地导入,并消除为您的类型和常量使用单独模块的混乱。

如果您想知道还有哪些其他 F2003 好东西可供您使用,只需查看每个更新的发行说明。每个问题都提供了受支持的 F2003 功能的完整列表。把它们都收集起来!”

于 2014-01-08T14:20:52.433 回答