- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I need to develop a general library subroutine that can be called with either a simple or an array argument, similar to the way generic intrinsic functions can be called with either real or integer arguments.
The subroutine SUB has access to a list of values, and calls to SUB may request either a single value, or an array of values.
In Fortran 77 this was easy, as in the following code:
PROGRAM TRYIT
REAL X
REAL XA (5)
! Call the routine with a simple argument to get a single value
CALL SUB (X)
PRINT *, 'Returned value X =', X
! Call the routine with an array argument to get multiple values up to 5
CALL SUB (XA)
PRINT *, 'Returned values XA =', XA
END
SUBROUTINE SUB (GENERAL)
DIMENSION GENERAL (100) ! Library capacity is up to 100 values
N_NOW = 3
DO I = 1, N_NOW
GENERAL(I) = 10. * I
END DO
END
Program output:
Returned value X = 10.000
Returned value XA = 10.000 20.000 30.000 0.000 0.000
Which is exactly what I want. This worked because the compiler did not enforce type correspondence for arguments. So, the simple real argument Xacted as a pointer to the first element of the dummy argument array. I believe this was a quite common and widely used feature.
In Fortran 90, or at least my configuration of the IVF compiler, this feature is not allowed. Is there some more modern, formal way of accomplishing the same thing? Possibly involving pointers or interface blocks (which I know little about)?
Thanks to anyone whocan give me a concise sample code.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
SUBROUTINE SUB (A_1, A_MULT)
REAL, OPTIONAL :: A_1
REAL, OPTIONAL :: A_MULT(100)
IF (PRESENT (A_1)) THEN
...
! Return the single value
...
IF (PRESENT (A_MULT)) THEN
...
! Return the array of values
...
ENDIF
END
This seems like a good solution to me, but I'd still be interesed in other ways.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Maybe you want a generic interface?
[fortran]MODULE my_library_routines IMPLICIT NONE PRIVATE PUBLIC :: sub INTERFACE sub MODULE PROCEDURE sub_scalar MODULE PROCEDURE sub_array END INTERFACE sub CONTAINS SUBROUTINE sub_scalar(a) REAL, INTENT(OUT) :: a !**** a = 99.9 END SUBROUTINE sub_scalar SUBROUTINE sub_array(a) REAL, INTENT(OUT) :: a(:) !---- INTEGER :: i !**** DO i = 1, SIZE(a) ; a(i) = REAL(i) ; END DO END SUBROUTINE sub_array END MODULE my_library_routines PROGRAM test_it USE my_library_routines IMPLICIT NONE REAL :: a_scalar REAL :: an_array(10) !**** CALL sub(a_scalar) PRINT "('a scalar:',F10.1)", a_scalar CALL sub(an_array) PRINT "('an_array:',999(F10.1,:,1X))", an_array END PROGRAM test_it[/fortran]
(The module keyword inside the interface block is optional as of F2003, but ifort doesn't support that as of 12.1.)
Also, ELEMENTAL procedures may take scalars or arrays (of any rank) - the procedure is called for each element in the array (or just once for the scalar). In this case there is no way for an elemental procedure that is called with an array to find out the size (or shape) of the array that it has been called with - it always sees scalars.
Optional arguments (and elemental procedures) require the procedure to have an explicit interface (it needs to be in a module or you need to have manually provided an interface block for the procedure).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm confused by your comment--which also appears in the reference documentation--that "optional arguments require an explicit interface." I find that it is not always required, viz., if there is oneoptional argument and it isat the end. For example, a way of modifying my code is:
! Call to get a single value
CALL SUB (A_1)
! Call to get multiple values
CALL SUB (A_1dummy, A_MULT)
SUBROUTINE SUB (A_1, A_MULT)
REAL :: A_1
REAL, OPTINAL :: A_MULT
IF (PRESENT (A_MULT)) THEN
...(Ignore A_1 and just return A_MULT)...
ELSE
...(Return A_1 only)...
ENDIF
Not as elegant perhaps, but quite adequate, and does not require a module or an interface.
I need to study the module approach more to understand it. I had thought that interfaces were not needed for procedures in a module; they were provided by default. Your solution involves both a module AND an interface, and I realize this is the "generic interface" approach. Maybe there is another approach involving a procedure with two optional arguments (perhaps referenced by keywords) provided via a module?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You asked for a suggestion which would switch between scalar and array argument without violating the standard, and the suggestion of generic interface was the answer. It's true that a scalar argument works the same under the hood as an array length 1 with ifort and other typical implementations on the same architecture, if you defeat standards checking, but many of us remember platforms where that wasn't so.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The explicit in "explicit interface" does not (necessarily) mean "I the programmer explicitly provided it". Instead, what it means is that "the compiler has been able to determine explicitly the interface". The opposite concept is "the compiler implicitly determined what the interface to the procedure was, based on how the programmer called the procedure".
So you are right - procedures in a module (and internal procedures as Tim points out) automatically get an explicit interface - after compiling the module (and hence wherever the module is subsequently USE'd - which must come later in the compilation process) the compiler "knows" things such as the number and type of the arguments that the procedure takes and any attributes that each argument might have.
Procedures that don't get an automatic explicit interface can be given one by the programmer using an interface block.
But interface blocks (things that start with an INTERFACE statement) can be used for more than just providing explicit interfaces for procedures that aren't module procedures or internal procedures.
- If the interface statement consists only of keyword INTERFACE, then you are defining explicit interfaces for procedures that don't otherwise have them.
- If the interface statement has a name after the interface keyword, then it defines a generic interface. The one name may refer to a number of procedures, which must be unambiguously distinguishable in some way by their type, kind, rank, number, etc.
- If the interface statement has the ABSTRACT keyword before the INTERFACE keyword, then it defines an abstract interface - which is a bit like a template for what another (real) procedure must look like somewhere else (perhaps a procedure that is being passed as an argument or a type bound procedure). (The procedure referenced in the abstract interface doesn't itself exist, though ifort 12.1 struggles with that concept under some situations...)
Consider your use of optional arguments without an explicit interface. Typical implementation by a processor is that for the arguments that are missing, a placeholder value or flag (often a zero address) is passed instead of the argument - inside the procedure the PRESENT intrinsic just checks to see whether the placeholder is zero or a real address. But this means that at the point of the call the compiler must know (have explicit knowledge - have an explicit interface) how many arguments the procedure has, otherwise it doesn't know to pass the appropriate placeholder. If the compiler hasn't made arrangements at the point of call for the "the argument is missing" placeholder to be there, then the PRESENT intrinsic is just going to be checking some random garbage in memory. There would then be some very dark storm clouds gathering on the horizon of your program...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Though I believe in abiding by the rules, I struggle to learn them because (a) documentation is at times not very clear or simple to follow, leading me to experiment a lot by trial and error, (b) the trial and error process is sometimes flawed by compilers that sometimes allow things to work even when they don't conform to the standard.
The discussion in this forum has been very beneficial to my understanding.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The compiler we had could do something like this
SUBROUTINE ADD_ONE(I)
INTEGER I
I = I + 1
END SUBROUTINE ADD_ONE
...
CALL ADD_ONE(100)
J= 100
The value of J is 101 (IOW the ADD_ONE modified a literal)
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
![](/skins/images/45C6C2D737ED71F4C51F0145C8CB1E9C/responsive_peak/images/icon_anonymous_message.png)
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page