Fortran中INTERFACE块和MODULE过程有什么区别?

10

我对在模块内使用接口块以及使用CONTAINS语句来为模块内的过程创建“显式接口”有些困惑。

我通常会在模块内使用接口块来编写过程。例如:

    MODULE ModExample
    INTERFACE 
        SUBROUTINE Sumatory(a, b, c)
            IMPLICIT NONE

            INTEGER, INTENT(IN)::a
            INTEGER, INTENT(OUT)::b
            INTEGER, INTENT(OUT)::c
        END SUBROUTINE Sumatory
    END INTERFACE
    END MODULE ModExample

   SUBROUTINE Sumatory(a, b, c)
      IMPLICIT NONE

      INTEGER, INTENT(IN)::a
      INTEGER, INTENT(OUT)::b
      INTEGER, INTENT(OUT)::c

      !Executable statements here

   END SUBROUTINE Sumatory

对于我来说,这很有效。但它也可以使用模块内的CONTAINS语句编写,实际上这是我咨询过的Fortran书中示例程序编写的方式。

MODULE ModExample

CONTAINS

SUBROUTINE Sumatory(a, b, c)
    IMPLICIT NONE

    INTEGER, INTENT(IN)::a
    INTEGER, INTENT(OUT)::b
    INTEGER, INTENT(OUT)::c

    !Executable statements here

END SUBROUTINE Sumatory
END MODOULE ModExample

那么INTERFCE块有什么问题吗?两者是否等效?我应该使用哪种方法?也许所有这些问题都可以用一个大的"取决于"来回答,但我希望你能解释它们之间的区别。提前谢谢。

1个回答

15

视情况而定,但除非有充分的理由否则请使用模块程序("在contains之后")。

第一种方法的问题在于您不得不两次指定过程的接口 - 一次在接口块中,另一次在过程定义本身中。而在第二种情况下,接口只在过程定义中指定一次。需要维护多个规范是潜在错误的来源。

详细说明:

在第一个代码示例中,位于后一个SUBROUTINE和END SUBROUTINE语句之间的源代码(未包含在接口块中)称为外部子程序。这是一个独立的程序单元。外部子程序定义了外部过程。

在第二个代码示例中,出现在CONTAINS语句后面的SUBROUTINE和END SUBROUTINE语句之间的源代码是模块子程序。它是模块程序单元的一部分。该模块子程序定义了模块过程。

( "subprogram"是指源代码构造,而"procedure"是指源代码定义的东西。)

还存在内部子程序(它们出现在外部或模块子程序或主程序的CONTAINS语句之后),它们定义内部过程,以及单独的模块子程序,这是另一种定义模块过程的方式。

Fortran程序单元(主程序、模块、子模块、外部子程序、块数据)使用"分离编译"模型。在编译特定的程序单元时,编译器会像不知道程序中的其他程序单元一样操作,除非源代码中明确规定。

这意味着,如果您在作用域中引用外部过程而未明确告诉编译器该外部过程的接口,那么编译器必须从引用方式隐式推断外部过程的接口(该过程具有"隐式接口")。以这种方式引用的过程无法使用语言的许多新参数传递特性(因为编译器不知道如何正确调用和传递参数到过程)。实际上,编译器也不太可能识别不匹配的参数类型等错误。
可以使用界面块(例如第一个代码示例中的界面块)来显式指定外部过程的接口。在源代码中引用外部过程并且该"显式接口"可用时,可以使用所有现代参数传递特性,并且很可能获得更好的编译器错误检测。但是,程序员仍然有义务确保接口主体和实际外部过程定义的相关特征是一致的。
该语言还要求只能在作用域单元中访问一个过程的接口。在定义它的外部子程序中,该过程的接口已经是显式的,因此程序员有责任确保不会在外部过程内部访问相同外部过程的接口主体。
允许在程序单元之间共享信息的其中一个显式规范是USE语句,它使得模块中定义的事物的知识在出现USE语句的范围内可用。这包括有关模块定义或声明的过程的知识。
与外部过程不同,模块过程或内部过程的接口始终在其标识符可访问的作用域中是显式的-无需为模块过程或内部过程编写接口主体(除了单独的模块子程序,您不应具有接口主体)。
总之:

第一个例子 - 你有一个模块,其中包含外部过程的接口定义和外部过程本身。你可以在不需要使用该模块的情况下引用该外部过程,此时会使用隐式接口(功能受限,容易出错)。或者,如果该模块在引用范围内被use,则接口将是显式的。在这种情况下,程序员必须确保接口体和外部过程定义匹配,并且外部过程的接口体不能在外部过程内部访问。这容易出错,也会增加维护工作。

第二个例子 - 你有一个具有模块过程的模块。你无法通过名称引用该模块过程而不use相关模块。对于这样的引用,接口始终是显式的。不需要维护单独的接口体以供过程使用。

只有当你需要打破编译依赖循环或限制长的编译依赖链时,才有理由使用第一种形式而不是第二种形式。


3
使用接口体的另一个原因是描述你没有 Fortran 源代码的代码,例如通过 ISO C 绑定调用的 C 代码。 - M. S. B.
我之前没有意识到,即使在没有使用声明INTERFACE块的模块的情况下,我仍然可以调用该过程。 - Granados

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接