Doctor Fortran Gets Explicit - Again!

Nearly 11 years ago (!) I wrote an item for the Visual Fortran Newsletter on explicit interfaces in Fortran. In recent weeks, I have had to refer quite a few customers to this article, suggesting that many Fortran programmers don't understand the role and rules of explicit interfaces. However, when I reread the item, I realized that things had changed a bit since Fortran 95, so I figured it was time to revisit the issue.

In Fortran-speak, an "interface" is information about a callable procedure. Fortran 77 had only "implicit interface" where the only thing you could say about a procedure was the datatype of a function result. While the language said that arguments in a call must match in number, order, type and rank (number of dimensions), there was no way to describe the arguments so that the compiler could check them. Furthermore, a compiler did not need to know these things because it didn't affect how arguments were passed.

Enter Fortran 90. Suddenly, things get a lot more complicated. For example, a dummy argument could be an assumed-shape array, requiring the call to supply information about the array bounds. Arguments could be OPTIONAL, requiring the caller to somehow indicate that an argument was omitted. Even more fun, a function could return an array or a character value whose length was dependent on some function of the arguments. All of these things, and more, required the ability to describe in detail the procedure and its arguments so that the compiler could call the procedure the right way. This information also helped correctness as the compiler could now check to make sure you were passing the correct arguments. This set of information is called an "explicit interface".

There are several ways to provide an explicit interface. The simplest and best way is to put the procedure in a module, or make it a CONTAINed procedure of the calling program or procedure. This has the advantage of not requiring you to write the information twice and thus increasing the chances of getting it wrong in one of the places. When you have a module procedure, or a contained procedure, its interface is automatically visible to everything else in the module or in the parent scope. Assuming the name hasn't been declared PRIVATE, the interface is also available to places where you USE a module containing a module procedure.

There is an additional way of declaring an explicit interface, an INTERFACE block. In most cases, you should avoid writing INTERFACE blocks for Fortran procedures as it duplicates information, but sometimes it can be handy to do so if you are updating an older program. Just remember that the language does not allow "interface to self" - that means, you can't have an interface visible in the scope where the procedure itself is defined. Note that my admonition to avoid INTERFACE blocks doesn't apply when you want to declare a generic interface as long as you use a MODULE PROCEDURE or PROCEDURE declaration in the list of specific routines in the generic interface.

In many cases, especially if you are sticking to abilities that were in Fortran 77, you don't have to provide explicit interfaces, though it's a good idea to do so. But, as I explained in my 2001 article, there are circumstances where the language requires you to have an explicit interface visible to the caller. The list of things that trigger this requirement grew in Fortran 2003 and again in Fortran 2008. Here is the current list (section 12.4.2.2 of Fortran 2008 if you want to follow along):

A procedure other than a statement function shall have an explicit interface if it is referenced and

  1. a reference to the procedure appears
    1. with an argument keyword, or,
    2. in a context that requires it to be pure,
  2. the procedure has a dummy argument that
    1. has the ALLOCATABLE, ASYNCHRONOUS, OPTIONAL, POINTER, TARGET, VALUE, or VOLATILE attributes,
    2. is an assumed-shape array,
    3. is a coarray,
    4. is of a parameterized derived type, or
    5. is polymorphic,
  3. the procedure has a result that
    1. is an array,
    2. is a pointer or is allocatable, or
    3. has a nonassumed type parameter value that is not a constant expression,
  4. the procedure is elemental, or
  5. the procedure has the BIND attribute.

(Statement functions and an assumed-size function result are features that are deemed obsolescent or "deprecated" )

The most common mistake made in not providing an explicit interface is when calling procedures with OPTIONAL or POINTER arguments. Programmers read about these features and decide to add them to their F77-style code, and then complain when the program dies with errors such as access violation. But how can you find the places where you need an explicit interface? Intel Fortran is here to help!

Since version 9.1, the Intel compiler has supported a feature called Generated Interface Checking. If this is enabled and the compiler sees an "external" procedure (one not in a module or otherwise CONTAINed), it generates an interface based on the attributes it sees and saves it in a compiled module file (.mod) with the name of the procedure followed by "__genmod.mod". It also generates a .f90 file with a human-readable version of this interface, but this is not used by the compiler. Then, when it comes across a call to a procedure for which there is not already an explicit interface, it looks to see if there is a generated one. NOT as a substitute for where the language requires it, but so that it can compare the call to the generated interface and complain if there are any errors. These errors could include calling with the wrong number or types of arguments, or a situation where the language requires an explicit interface but none is provided.

On Windows, Generated Interface Checking (Fortran > Diagnostics > Check Routine Interfaces) is on by default in the Debug configuration of newly created Visual Studio projects. From the command line, you can enable it with /warn:interface on Windows and with -warn interface on Linux and Mac OS. I highly recommend using this option in your builds. It is not perfect, however. In particular, it is sensitive to the order in which files were compiled. If a source that calls a routine is compiled before that of the source that defines the routine, there is no generated interface to check. There can also be an issue if you are editing sources and don't do a "clean" build as there may be an interface from an earlier version of the source still around. And then there are the occasional bugs, though these are rare nowadays.

In summary, it is good Fortran practice to have explicit interfaces for all your procedures. The best way to accomplish this is to have all procedures in modules or as contained procedures if they will be used in limited contexts.

有关编译器优化的更完整信息,请参阅优化通知
类别: