Programming Queries

Programming Queries

dannycat's picture

I've been struggling with an issue for about a week now with a program that I used to compile using CVF for the 32-bit Release Version and IVF 11.1.070 for 64-Bit Debug and Release versions.

At the end of last week my Visual Studio 6 died on my 64-bit XP Machine. I've tried reinstalling it but it still crashes everytime I try to open a file. The project can be selectedbutVS6 also crashes when loading project files.

CVF had been working quite happily on the same computer for over a year. The reasons Icontinued touse it were mainly because I prefer the resource editor in VS6 to that in VS2008 and the 32-bit application is the one used by my customers for over ten years.

For customers with 64-bit machines I have successfully used IVFand both variants of the program show consistency in their function and output etc.

My first thought was to simply finally drop CVF and switch to IVF for all versions (something I meant to do anyway so that new features can be adopted in the future releases) but unfortunately the IVF 32bit Release compiled version of my program crashes.

32bit debug, 64bit release & debug versions all seem to work fine. I uselots of allocatable user defined types that have allocatable components and it is during deallocaton of one of these that the program crashes. I identifid the cause by adding write statements at various places within the code.

During my debugging sessions (on release configuration) I came across a few dodgy programming quirks that I would like to discuss.

For all dtat types I write a module that manages all instances within the program. The module contains routines to allocate, copy instances (using operator module procedure), increase/decrease size (not using reallocate) and deallocate.

1) Is using the UBOUND function on an unallocated array or data structure a safe thing to do? It returns 0 which suggests it works but maybe inconsistent on other computers.

2) What does allocating something ALLOCATE (x(0)) actually mean? This caused problems on CVF as the ALLOCATED(x) result would have been .false. but DEALLOCATE(x) would crash the program. I have no suchdeclarations in my code that I am aware of. ShouldIVF allow this.

3) In order to initialise a data structure I have a single instance of the structure with all components initialised as required. In the case of allocatable components I allocate to a minimum size. I then use a single statement to initialise the whole array. eg

type DATAT
integer :: b
real ::a
end type

type(DATAT),allocatable :: x(:)
type(DATAT) :: x0

x0%a = 0.0
x0%b = 0

allocate(x(100)) ! Line XX
x = x0

However if for some reason you don't firstly allocate x, eg by removing Line XX the program still allows the
x = x0. Is this correct?

4) optional arguments.Should the following statement be allowed? If not could either compiler or run time error be issued?

subroutine XXX(a,b)

integer,intent(in) :: a
integer,intent(in),optional :: b

if(present(b).and.b.gt.0) then

! Do something

else

! Do something else

endif

Although I've not got a solution to my problem yetI thought I'd bring these to everyone's attention for comment.

Any further advice would be much appreciated.

Steve

30 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.
IanH's picture
Quoting dannycat 1) Is using the UBOUND function on an unallocated array or data structure a safe thing to do? It returns 0 which suggests it works but maybe inconsistent on other computers.

No. F2008 13.7.171: ARRAY (the argument to UBOUND) ... shall not be an unallocated allocatable array or a pointer that is not associated.

The processor isn't required to diagnose this, but with ifort using /check:all I'd expect a runtime error.

2) What does allocating something ALLOCATE (x(0)) actually mean? This caused problems on CVF as the ALLOCATED(x) result would have been .false. but DEALLOCATE(x) would crash the program. I have no suchdeclarations in my code that I am aware of. ShouldIVF allow this.

It allocates an array of zero size. Note the array is still allocated (just as if it was allocated with a non-zero size) - the behaviour of CVF that you describe would be incorrect.

Zero size arrays are standard conforming (F2008 6.7.1.2). I use them quite a bit in IVF.

3) In order to initialise a data structure I have a single instance of the structure with all components initialised as required. In the case of allocatable components I allocate to a minimum size. I then use a single statement to initialise the whole array. eg

type DATAT
integer :: b
real ::a
end type

type(DATAT),allocatable :: x(:)
type(DATAT) :: x0

x0%a = 0.0
x0%b = 0

allocate(x(100)) ! Line XX
x = x0

However if for some reason you don't firstly allocate x, eg by removing Line XX the program still allows the
x = x0. Is this correct?

No. F2008 7.2.1.3: If the variable (left hand side of the assignment) is an unallocated allocatable array, expr (the right hand side) shall have the same rank (in your example the expr is a scalar (rank zero) while x is rank one).

The processor isn't required by the standard to diagnose this (but ifort should with /check:all).

Note (with the /standard-semantics flag) that x = [x0] (i.e. the right hand side is an array expression) is ok if x is unallocated - in that case x will be automatically allocated to a size one array for you, with the value of the single element being x0. You can use array constructors and intrinsics like SPREAD/RESHAPE/etc to allocate and assign more complicated examples in one step (personally I wouldn't bother here - I'd just stick with the allocate then assign that you have, but sometimes it can be convenient).

4) optional arguments.Should the following statement be allowed? If not could either compiler or run time error be issued?

subroutine XXX(a,b)

integer,intent(in) :: a
integer,intent(in),optional :: b

if(present(b).and.b.gt.0) then

! Do something

else

! Do something else

endif

The code doesn't conform (assuming a call is made without b present). The order of evaluation of the operands to the .AND. operator in the expression in the if statement is not specified. The compiler could evaluate the b .GT. 0 part before the PRESENT part (if it was really clever it could evaluate them in parallel - it could also use a coin toss to work out which one to evaluate first (the order could change from run to run)). If an optional argument isn't present then you can't reference it in that way (F2008 12.5.2.12 - basically all you can do is use the PRESENT intrinsic or pass it on to another procedure as an optional argument).

The processor isn't required to diagnose this. If it did evaluate the b .GT. 0 bit before PRESENT, then with ifort with /check:all I think you'd get a runtime error message about an NULL pointer access.

Note that because it uses optional arguments XXX would need to have an explicit interface (it needs to be a module procedure or have an INTERFACE block, etc) - F2008 12.4.2.2.

dannycat's picture

Thanks Ian,

Your replies were a great help. I hadn't used the check:all directive (only check:bounds). Now when I changed to check:all it has thrown up some interesting things:

1) It complains when usingNULL_RECT in the Validate and Invalidate API functions. I now have to obtain the actual window Rect and pass this instead. What is wrong with using NULL_RECT as documentation states that this implies the full window?

2) Whenever I read an array section like:

real :: array(100,8)

do i = 1 , 100
read(66) array(i,1:8)
enddo

I get a warning about a temporary array being created. I know I can turn this check off but in what circumstances would you need to use it?

Steve

Tim Prince's picture
Quoting dannycat

2) Whenever I read an array section like:

real :: array(100,8)

do i = 1 , 100
read(66) array(i,1:8)
enddo

I get a warning about a temporary array being created. I know I can turn this check off but in what circumstances would you need to use it?

A purpose of the temporary array warning is to help you judge inefficiencies. You might argue that the inefficiency is obvious in this case, where you convert a group of 8 elements, then require an entire cache line to be updated for each move of an element from the temporary to the declared array. If the array were too large for L1D cache locality, it could be a significant performance issue.

mecej4's picture

If you execute the segment of code that reads unit-66 only a couple of times, the warning can be ignored. On the other hand, if you read many files, or read a large file, the following considerations should be noted.

If you want to read the whole array with unformatted reads, it would be much more efficient to use instead of your DO loop the statement

READ(66) array

However, the data file would have to be rewritten to match this READ statement.

If you wanted to fill only a part of the array, it would be more efficient to read by columns, for example:

DO J = 1, 7
READ(66) ARRAY(i,j), i=1,80)
END DO

Again, the data file would have to be rewritten.

John Campbell's picture

A good way to initialise a data structure is to also declare a paramater type that is zero or initial values as you declare the TYPE structure. I'm not sure if the SEQUENCE attribute is also required or is a default for the compiler that I use.
For example:

type DATAT
integer :: b
real :: a
end type
type (DATAT), parameter :: DATAT_zero = DATAT (0, 0.0)
!
type(DATAT),allocatable :: x(:)
type(DATAT) :: x0
!
x0 = DATAT_zero
!
allocate (x(100)) ! Line XX

x = DATAT_zero

This is a clean way of defining the initial setting, as you define the TYPE structure. Other named settings for a type structure can also assist in documenting their use.

John

Steve Lionel (Intel)'s picture

If you're doing a derived type, you can put the initialization right in the type declaration. For example:

type DATAT
integer :: b = 0
real :: a = 0.0
end type

Now everything of that type will be initialized.

This usage has an interesting and unique (to the language) side effect. Anyone know what it is?

Steve
acar's picture

No I don't but I'd be interested to know!

Steve Lionel (Intel)'s picture

I'll give it another day to see if anyone comes up with it.

Steve
mecej4's picture

I'll try: unlike initializations given in variable declarations or DATA statements, initialization of some or all components in the TYPE definition need not imply SAVE?

Steve Lionel (Intel)'s picture

That's not exactly what I was thinking of, but it is related and is interesting in its own right. I'll give a hint - it has to do with dummy arguments of such a type.

Steve
mecej4's picture

This test program is founded on your hint, and shows that a variable of such a type, when passed to a subroutine which has the corresponding dummy declared INTENT(OUT) receives the default initialization. Without default initialization, components of such variables become undefined upon entry to the subroutine.

program tease
  type DATAT
    integer :: b = 1
    real :: a = 2.0
  end type

  type(DATAT) :: v

  call sub(v)
  write(*,10)v%a, v%b
  10 format(' a = ',F8.4,'  b = ',i3)
  stop

  contains

  subroutine sub(w)
  type (DATAT), intent(out) :: w
  w%a = 3.0              ! Note w%b set by def. initialization
  return
  end subroutine sub

end program tease
Steve Lionel (Intel)'s picture

Yep, that's it. It's the only case where a dummy argument with INTENT(OUT) does not start out in an undefined status. I will offer the caveat that this applies to non-pointer dummy arguments only.

What's more interesting is that this initialization will occur on every call to the procedure.

But what's maybe more interesting, and I had forgotten about this myself, is that it also applies to local variables of such a derived type. Unlike "static initialization", the components of such variables will get re-initialized on each call.

Steve
John Campbell's picture

I would suggest that the use of derived type parameters, such as DATAT_zero or DATAT_one provide a moretransparent way of initialising derived types.

Your question prompted me to look at the changes to Fortran since 1995. There is a lot of non-fortran in 2003 !

John

arjenmarkus's picture

What do you mean by "non-fortran"?

Regards,

Arjen

John Campbell's picture

Arjem,

Steve's question sent me looking at the 2003 standard and then I reviewed an article "The New Features of Fortran 2003" by John Reid.
I have been using Fortran continually since 1973 and I'dexpect that other programmers of my vintage would find it difficult to appreciate where these new features in 2003 can be used. I think more in terms of numerical computation, such as equation solutions and least squares analysis of data sets. I typically choose a precision suitable for a numerical approach and use subroutines to handle different data structures. I don't think in terms of pointers and now procedure pointers. While I've read the term "polymorphic", I have little understanding of it or less idea of how and why I'd use it. In short I don't understand why these developments have taken place. I don't understand where Fortran is going. It doesn't appear to make my work any easier.
The problem I have is I do good work and it is of benefit in the industry I work. With the apparent emphasis in fortran development, I don't see any attention to the types of fortran usage I do and don't see new engineers educated with the type of skills I have. In 10 years who will be able to do this type of work, as polymorphic entries aren't very relevant to the numerical computation I do. Maybe the solution is they'll all use Matlab and hope the codes don't get lost. More likely, the work I do will be done differently in the future.
There have been problems in the ease of use of fortran on new types of hardware but the areas of progress don't appear to address these problems, such as the graphics interfaceand the use of new style multi-processors. (My recent attempt to use parallelization with ifort Ver 11 was not a success, althouigh Ver 12 may be better.)
To me, Fortran development has been sidetracked by non-fortran users. It's probably our own fault for not being more involved. Those who have progressed the language have put in a lot of effort. It now looks a lot more like C.. and probably with all the bugs that complex code structures provide. The KISS principal, which I was taught a long time ago appears to have been lost.

John

arjenmarkus's picture

I have been using Fortran since 1982 or thereabouts, but I have also been using several other
programming languages. I can sympathesize up to a degree with your feelings about Fortran 2003
but on the other hand I do appreciate these new features too.

One possibility that Fortran 2003 offers that can not be so easily solved in previous versions is
designing library interfaces. Metcalf, Reid and Cohen give an example of this. It takes a bit of
getting used to perhaps, but it does help to have type-bound procedures in that case.

You also touch upon a wider issue - the whole matter of numerical computations."Computing" today
involves much less numerical computing than it does constructing web pages with a nice look and
feel. But that is a different story than the original subject of this thread.

Regards,

Arjen

mecej4's picture

John, it is not at all unusual to be put off by unfamiliar features in a programming language and to have misgivings about the cost/benefit aspects of learning and using those new features in one's work.

Fortran, however, leaves you in control of change. You can (with almost no exceptions) use the subset of Fortran 2xxx that is the same as Fortran 77 (less a few deprecated features). If you wish, and only if you do, you may gradually introduce some of the new features, after you have convinced yourself of the benefits, and at your pace.

Here are two related features that I have found very useful in removing bugs in Netlib code (those are rare, but when they exist they can be nasty). That they exist, even in code written by highly competent mathematicians, show that Fortran-77's reliance on the programmers to do the right thing was not quite justified.

Feature 1: Declaring and verifying INTENT of subroutine and function arguments.

Feature 2: Abstract procedure declarations. ANSI C provided the corresponding capability in 1978. We have had to wait until F2003 for this feature, which is essential if we are to write robust, reusable libraries of numerical methods. Consider as an example a nonlinear equation solution routine:

Call Solver(Func,x,Iter,Eps)

which you want to call with different Func-s at different times. In Fortran-77, you specify Func as EXTERNAL, which allows the compiler to recognize that Func is not just another variable and needs to be processed by the linker. However, there is no way in Fortran-95 to check that a specific Func that you use in this call has the correct type and argument list, unless you write an interface block for every instance of Func that you wish to use. Errors in function result type are easy to detect and fix, but errors in arguments are notoriously difficult to remove.

In contrast, ANSI C lets you write

extern void Solver(double (*Func) (const double), double *x, int *Iter, double Eps));

You can see a short example of how abstract procedure declarations let you do the equivalent in Fortran 2003 in this recent thread: "Compiler error - 12.0 Update 2".

arjenmarkus's picture

mecej4,

I do not quite agree here:

subroutine solver( func, x, iter, eps )
real :: x, eps
integer :: iter

interface
real function func(z)
real :: z
end interface

...
end subroutine solver

is perfectly (modulo typos) acceptable Fortran 90.

It gets nastier if you need to pass arbitrary data to the function - then type-bound procedures
as I referred to get interesting.

Regards,

Arjen

mecej4's picture

Things are, indeed, fine at the receiving end of the CALL, which is the part that you showed.

Suppose you have three or four calls to Solver with different functions that are also in different source files than the line in which the call is made. How do you ensure that every one of these functions has the correct interface, even if all that we have are implicit interfaces, without using compiler features such as the /warn of IFort?

John Campbell's picture

mecej4 and Arjen,

Thanks for your comments.
I like to think there is still a need for numerical computation.
Certainly the size and speed of Fortran computation allows for more accurate modelling.
The vast range of computational techniques available in alternatives like Matlab and even Excel is impressive. Fortran still allows for investigating the model assumptions.

You have highlighted two features, which I think date back to F90.
I find the use of INTENT and INTERFACE more useful for documentation, rather than assisting in providing bug-free code.
To me INTERFACE fails, as it merely documents the expected routine.
There is no guarantee it agrees with the latest version of the routine being called, especiallywhen developing code.
I've wanted the INTERFACE definitions to beincluded in a module, which can also be included in it's own routine.Rather than an error, the interface should be checked against the routine's declaration or better still, used as the routine's declaration. That way, similar to a .lib file, the compiler could check the use of each routine.
INTERFACE is a duplicate definition. If you change the routine, then you have to search all the code that could possibly call it.
Contrast this to MODULE which allowed a single definition of variables where previously INCLUDE/COMMON required dual definition. This has removed a significant source of obscure errors.
Duplication of code definition is a major source of errors when developing software. INTERFACE should only be allowed in a MODULE, rather than being declared each time the routine is called.MODULE was a significant advance at F90.

The number of subroutine arguments has always been a significant source of error. I'm not sure that INTENT has addressed this effectively.
It's a shame a simple argument count check has never been included for run time or linking. I know there are times it can not be used, but they are not the norm.
The call argument count should be checked against the routine at link or run time.
INTENT also has some side-effects as shown by the main part of this thread, especially if OUT is chosen.
Selecting INTENT (IN) or (OUT) often can highlight cases where it is not intended, with the need to revert to INTENT (IN OUT) and no significant improvement in the code.

My experience has shown Fortran to be easier to develop near bug-free code. My past performance has always been faster delivery of effective code, in comparison to other software sectors.
I'm always amazed at the amount of bug-riddled code there appears to be elsewhere, although publicly available packages can suffer from an unfriendly user-base.

As to the use of an EXTERNAL function as a subroutine argument, I last did that in an assignment in 1973. I prefer the use of an argument option to select from a range of explicitly coded functions, now via a SELECT CASE structure. I have access to all the code so don't need the flexibility.

John

IanH's picture
Quoting mecej4 Things are, indeed, fine at the receiving end of the CALL, which is the part that you showed.

Suppose you have three or four calls to Solver with different functions that are also in different source files than the line in which the call is made. How do you ensure that every one of these functions has the correct interface, even if all that we have are implicit interfaces, without using compiler features such as the /warn of IFort?

I'm not sure I follow.

If the procedure being passed as an actual argument has an explicit
interface then the processor can (should, and does with ifort) check
that the characteristics of that explicit interface match an explicit interface provided for the dummy argument. It doesn't matter whether the dummy argument
has been declared as per arjen's F95 method or by a
PROCEDURE(abstract_interface) :: arg statement.

If the procedure being passed is external then the only way it can get an explicit
interface (from a language point of view - ignoring compiler
/warn:interface stuff) is if the programmer tells the compiler "trust me
- this is the interface for this function". If the programmer gets
that wrong they are in trouble - whether they use
PROCEDURE(abstract_interface) :: external_proc or the full interface
block.

I guess when you have multiple external procedures that you want to bless with the same
explicit interface then using an abstract interface does save some
typing and is less error prone as you only specify the full interface
block once.

But then why are the procedures external in the first place? Just make every procedure a module procedure. But you know that...

John - many of the problems you describe with checking of argument numbers (and more), duplication of code, etc, disappear if you use module procedures. If you could modify the procedures code to include a USE statement for its own interface (if that were legal), then I don't see what stops you modifying the code to put the procedure itself into a module.

I think the only external procedures I have are either defined by C (or something other than Fortran) or (very occasionally) where I've needed to break some sort of cyclic module dependency. In the first case a module full of interface blocks makes a lot of sense. The second case should hopefully disappear when submodules become available.

Also, you talk about using SELECT CASE to select from explictly coded functions. I imagine in this case the "functions"would take the same (or similar) variables and return the same sort of result, but vary in the details of the internal calculation? Or to put it another way - they have the same external interface but vary in their internal implementation? If so, then much of F2003 is just providing inbuilt language support for patterns of usage that you are already familiar with.

It's not hard to see what is essentially "polymorphism" in use in F77 code. For one example that some on this forum will be familiar with, the way "streams" (material and information flows) were implemented in a popular (populus?) chemical process simulator - where the stream could be of different types. An integer variable (hollerith actually) told you what the type of stream is, based on that type you then interpret differently the meaning of various elements of several real and integer arrays that were otherwise opaque. Some elements of those arrays had common meaning regardless of stream type, some elements were common to subsets of stream types. It worked, but any code written to interface with it all was a mess. You can do the same thing today with F2003's features in a way that is so much simpler and clearer.

mecej4's picture

> I'm not sure I follow.

Thanks for your comments; I have found it difficult to express myself clearly on this topic, perhaps because it is related to individual programming style (acquired from years of programming in F77 and C) not meshing well with new language features. I place emphasis on using libraries such as NAG and IMSL and making sure that procedure arguments passed to those libraries have the correct interface, which I realize is quite the opposite of John's position.

But, you have yourself provided the answer quite clearly, so I will merely quote you!

I guess when you have multiple external procedures that you want to bless with the same
explicit interface then using an abstract interface does save some
typing and is less error prone as you only specify the full interface
block once.

> But then why are the procedures external in the first place? Just make every procedure a module procedure.

Compilers often check implicit interfaces within a file (or all files compiled with one compiler invocation), and it is economical ( or lazy) to do it that way than write a separate module for those procedures in addition to writing the main driver program.

John Campbell's picture

IanH,

Your suggestion is interesting.

John - many of the problems you describe with checking of argument numbers (and more), duplication of code, etc, disappear if you use module procedures. If you could modify the procedures code to include a USE statement for its own interface (if that were legal), then I don't see what stops you modifying the code to put the procedure itself into a module.

I'm not sure where this would go. Like mecej4, I have always relied on static libraries when developing code, mostly my own. The implication would be to convert each library to a module and apply multiple use in the new code in each routine that requires the library modules. Each library module would then consist of the data definition then "contains" their routines.
The new code would then include USE statements for each "library" being called. I wonder how large and cumbersome the .obj files for the new code might become. Maybe the new compilers are smart enough to exclude all the unnecessary other info.
I'm yet to identify the benefits of CONTAINS, as I've always included the libraries at the link stage.

Would this approach guarantee argument checking ?

At present my use of ifort is in a dos box and my link command is a list of *.obj trees. I have not yet required the creation of static libraries. At present the libraries consist of multiple .for files, one for each procedure. Libraries appear to be an old-fashioned approach to programming. Isn't fashion great !!

Actually an easy way to implement this module approach might be to create a new file of the module and contains via a list of include statements. It will be interesting to see if I can identify any argument list anomalies.

The other alternative is to generate a new file "all.for" which is a list of INCLUDE 'xx.for' for all files in the project, although you could loose the flexibility of compilation options.

More argument checking at the link stage might have been a simpler approach, although this would not support mixed language programming. I don't think the Fortran language standard has properly addressed the linking process, although I am yet to fully read the 2003 and 2008 standards (or 1995).

John

IanH's picture

If a procedure is defined in a module (after the contains statement) then whenever that procedure is used then in practice you will get the best argument checking the compiler can offer. Some degree of that checking is mandated by the standard, in reality you will get a lot more than that. You get it automatically, there's no need for the creation of separate INTERFACE blocks. You also automatically can use all the new argument passing options and procedure characteristics that F90 and on offer - assumed shape, optional arguments, PURE procedures, steak knives, all "free". (Internal procedures get this automatically too).

If a procedure is external, then the standard places no requirements on a compiler for argument checking at all. Most compilers will do some with appropriate debugging flags, but because the information available to the compiler is less, it is never going to be better than with module procedures.

Apologies - its not clear to me whether the static libraries (.lib) that you are using are yours (you have control of the source code) or third party. If they are third party libraries then ignore some of the following....

One static library can contain the object code from many modules. You end up with one lib file (that archives together many obj files - one for each compiled source file that goes into the library) and many mod files (one for each module - I typically have one module per source file so in my case the number of obj files and mod files is of the same order).

It may suit, though, to make life easier for the prospective users of your static library (which might be yourself), to have a top level module that USE's a selection of the other modules present in the libraries' source code and exposes just the procedures that the library's users should be calling (worker routines etc are still in modules, but these are not modules that would be typically USE'd by client code of the library).

To be clear - I'm not suggesting one module that has every procedure listed after the contains (unless the code is small) - instead it's a heirarchy of modules. Typically the lowest level of the heirarchy would group closely related procedures into a module. What closely related means is a judgement call, though there is a restriction that you cannot have circular module dependencies (i.e. an arrangement where module A USE's B, B USE's C, C USE's A is out - a related issue is that the source file that defines a module needs to be compiled before any source files that USE the module). That restriction aside, you could have every procedure in its own separate module, but that would be a bit of an administrative nightmare. Whether you have multiple modules per source file, one module per source file or one module split across several source files (by INCLUDE'ing bits together, in which case you don't separately compile the included files) is also up to you.

Higher levels of the heirarchy (if present) USE the modules in the lower level. They may have their own module procedures, or perhaps they just group and expose procedures (and types and variables, etc) from the lower level in a convenient way.

If you are not putting your own code into static libraries then there's no real difference in use. If you compile and link with one command line then I think you need to list the files in the right order (lowest level of the module heirarchy first).

Note that mod files are just for the compiler. They are not required at the link stage. I don't think the fortran standard even mentions the concept of linking (or mod files for that matter - that's all up to the various "processor" implementations). Others would know better.

The only downside to modules that I am aware of is that "compilation cascades" will result in increase build times (you make a change to a module that is at a low level in the heirarchy, all source files with program units that USE that module above it in the heirarchy need to be recompiled). In practice, my release builds have interprocedural optimisation turned on anyway, so I'd be getting a compilation cascade anyway even if I wasn't using modules. For debug builds it is occasionally annoying (with hundreds and hundreds of source files), but I deal with it. The submodules feature of F2008 should help fix this (which is why we all wait with baited breath for ifort 12.1/13/14/15/16...)

jimdempseyatthecove's picture

>>If a procedure is defined in a module (after the contains statement) then whenever that procedure is used then in practice you will get the best argument checking the compiler can offer. Some degree of that checking is mandated by the standard, in reality you will get a lot more than that. You get it automatically, there's no need for the creation of separate INTERFACE blocks

I somewhat disagree.

I find it more effective to split the source file of what would normally be a module with CONTAINS at the contains (removing the contains). Then add to thetop portion any INTERFACE blocks for what was in the CONTAINS. In the bottom portion, insert USE top portion in every subroutine and function.

The benefit of this, is during development of a large solution/project, if the interfaces do not change, but you edit the code section, then you do not need to re-compile ever routine that USEes the interfaces. Re-link - yes, re-compile - no.

Jim Dempsey

www.quickthreadprogramming.com
John Campbell's picture

Jim,

The problem occurs when you change the argument list for a library routine. I try hard to avoid this but it does happen.
You then have to change the use of this routine in all projectsthat use this library routine. I would have in excess of 100 projects that use my own libraries, with most of them archived. The calling code is just as wrong as the INTERFACE code it contains. I need the discipline to USE a module of interface definitions that is defined with the library, not with the project. When recovering an old project and re-linking, you always hopethe programstill works and you can remember the changes that have taken place since it was developed.
I remember having this discussion with amember of the Fortran Comittee in 1985, relating to argument checking and the need to include the concept of linking in the standard. (Being on the other side of the earth, contact was infrequent and all we had then was fax) Back then the committee was constrained by the need to support old style mainframes from a major computer vendor. I don't think we now have a significant improvement in minimising these types of programing errors. Since then, Arrays has been the big change and MODULES some improvement on the industry prompted INCLUDE, but removing this error prone aspect of Fortran needed more direct action.
As for the origins of this thread,I can't see the implied initiation of Derived Types to be a good addition to the language for good programing techniques.
Anyway, this is what we have. Thanks for sharing your views.

John

IanH's picture
Quoting jimdempseyatthecove >>If a procedure is defined in a module (after the contains statement) then whenever that procedure is used then in practice you will get the best argument checking the compiler can offer. Some degree of that checking is mandated by the standard, in reality you will get a lot more than that. You get it automatically, there's no need for the creation of separate INTERFACE blocks

I somewhat disagree.

I find it more effective to split the source file of what would normally be a module with CONTAINS at the contains (removing the contains). Then add to thetop portion any INTERFACE blocks for what was in the CONTAINS. In the bottom portion, insert USE top portion in every subroutine and function.

The benefit of this, is during development of a large solution/project, if the interfaces do not change, but you edit the code section, then you do not need to re-compile ever routine that USEes the interfaces. Re-link - yes, re-compile - no.

In some cases maybe a variant of this is appropriate, but:

- You end up with "redundant" code - interface specifications in the module and then the subprogram specification section itself;

- You'd better be really careful that those interface blocks match the actual subprogram's code, because otherwise you've just landed yourself in a world of pain (the compiler will quite happily check calls to the procedure using an incorrect interface);

- With modern compilers implementing inter-procedural optimisation, re-linking means re-compiling to some extent anyway.

I say "variant", because I'm not sure if having an interface block for a procedure accessible inside the procedure that the interface block describes is legal. You might need to prevent use association of that procedures interface using an ONLY clause. Others may know better.

The code base subject to constant recompilation would need to be very large before I'd consider this worthwhile. If this sort of thing was on the table then I'd also be looking hard for options to reorganise the code base, such that development and testing could be undertaken on smaller pieces in isolation.

Submodules should help here - the code verbosity is more or less the same, but you get guaranteed checking of the declared interface in the module against the actual interface implemented in the submodule. Bring 'em on!

John Campbell's picture

IanH,

You said,
"I say "variant", because I'm not sure if having an interface block for a procedure accessible inside the procedure that the interface block describes is legal. You might need to prevent use association of that procedures interface using an ONLY clause. Others may know better."

I thought it is illegal. I'm suggesting it would have been a good alternative for the standard, such as in my example below:

module all_interfaces

 interface

   subroutine aa (v1, v2, v3)
     real*8 v1
     real*8 v2
     integer*4 v3
   end subroutine aa

   subroutine bb (v1, v2, v3)
     real*8 v1
     real*8 v2
     integer*4 v3
   end subroutine bb

  end interface

end module all_interfaces

subroutine aa

use all_interfaces

   v2 = v1 ** v3

   call bb (v1, v2, v3)

end subroutine aa


subroutine bb

use all_interfaces

   v2 = v1 ** (-v3)

end subroutine bb

Although I have only 2 routines, there could be many more, with each routine taking it's argument definition from the INTERFACE definition module. That way, all routines in this librarycould have a single interface definition module which could also be USEd in the code that uses the library.
The aim is to not duplicate variable definition, which the present INTERFACE structure does, which I think you appreciate with yout comment :-
"You'd better be really careful that those interface blocks match the actual subprogram's code, because otherwise you've just landed yourself in a world of pain (the compiler will quite happily check calls to the procedure using an incorrect interface);"

When I read all the new complexity in 2003, I don't think what I am suggesting would have been too out there, as it would certainly lessen the pain.
I wonder who uses these 2003 structures. Probably all those who use Fortran .net.

John

jimdempseyatthecove's picture

>>The problem occurs when you change the argument list for a library routine. I try hard to avoid this but it does happen. You then have to change the use of this routine in all projectsthat use this library routine. I would have in excess of 100 projects that use my own libraries, with most of them archived.

There are two issues at work here:

During development time, it is advantageous to eliminate the number of unnecessary builds. i.e. rebuilding files that USE a module where CONTAINS code may have changed but interfaces have not. In large projects, this can add minutes to each cycle of edit/build/debug, edit/build/debug, ...

After development, the code is (may not)generally not distributed (e.g. MKL), leaving just the INTERFACE file(s). If you use the two file technique, then you already have the interface-only files and you are not maintaining two copies.

When you concern is, as you exampled, adding an argument to a library call, I would suggest creating a generic interface, one with the extra argument(s), one without. The one withoutcallsthe routine with the extra argument andsupplies a default argument. This eliminates the need to edit the calls that have no need (or meaning)to supply the extra argument. When the call requires using the extra argument, then you add the argument to the call. In the cases where all calls must have the additional argument then require that (those) arguments on all INTERFACEs.

>>I can't see the implied initiation of Derived Types

It is there for those who want/need this type of initiations. Don't use it if you don't need it.

At times I can see a need for a CTOR/DTOR type of functionallity where a user supplied function/subroutine is (optionally) called at creation the distruction of object. Apparently you can use

type mytype
[...]
contains
FINAL :: finalize_subroutine
end type mytype

for dtor, but I am not sure about CTOR

You can use something like http://fortwrap.sourceforge.net/ for CTOR
But that is an indirect way (calling C++ to call back to FORTRAN to do the ctor)

A better method might be

type mytype
[...]
contains
INITIAL :: initialize_subroutine ! not in standard
FINAL :: finalize_subroutine
end type mytype

As the above would add orthoganality

I also found this: http://groups.google.com/group/comp.lang.fortran/browse_thread/thread/b3...

The above seems to indicate you can create a "member function" with same name as type namebut containing an argument list declared differently from the type data layout. Essentially making a generic interface out of the type name(I have not tried this as I do not have F2003/F2008)

Jim Dempsey

www.quickthreadprogramming.com

Login to leave a comment.