- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Let's consider the following subroutine:
SUBROUTINE MAIN_SUB(A,B)
USE INTERFACE_FOR_OTHER_SUB (module containing the interface definition for OTHER_SUB).
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: A
INTEGER,INTENT(IN),OPTIONAL :: B
...
Assume that MAIN_SUB calls another subroutine for which the argument Bis also optional. I suppose I need to do the following:
...
IF (PRESENT(B)) THEN
CALL OTHER_SUB(A,B)
ELSE
CALL OTHER_SUB(A)
ENDIF
...
END SUBROUTINE MAIN_SUB
In other words, OTHER_SUB is a subroutine which arguments are exactly as provided by MAIN_SUB.
The branching statement above works well when there is one optional argument. But what if my subroutine has, say, 4 or 5? It becomes a combinatorial problem to implement all the possible present/non present combinations for all optional arguments!
Am I doing something more complicated than necessary? Maybe there is another solution?
Thanks,
Olivier
SUBROUTINE MAIN_SUB(A,B)
USE INTERFACE_FOR_OTHER_SUB (module containing the interface definition for OTHER_SUB).
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: A
INTEGER,INTENT(IN),OPTIONAL :: B
...
Assume that MAIN_SUB calls another subroutine for which the argument Bis also optional. I suppose I need to do the following:
...
IF (PRESENT(B)) THEN
CALL OTHER_SUB(A,B)
ELSE
CALL OTHER_SUB(A)
ENDIF
...
END SUBROUTINE MAIN_SUB
In other words, OTHER_SUB is a subroutine which arguments are exactly as provided by MAIN_SUB.
The branching statement above works well when there is one optional argument. But what if my subroutine has, say, 4 or 5? It becomes a combinatorial problem to implement all the possible present/non present combinations for all optional arguments!
Am I doing something more complicated than necessary? Maybe there is another solution?
Thanks,
Olivier
Link Copied
8 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quoting - opmkl
Let's consider the following subroutine:
SUBROUTINE MAIN_SUB(A,B)
USE INTERFACE_FOR_OTHER_SUB (module containing the interface definition for OTHER_SUB).
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: A
INTEGER,INTENT(IN),OPTIONAL :: B
...
Assume that MAIN_SUB calls another subroutine for which the argument Bis also optional. I suppose I need to do the following:
...
IF (PRESENT(B)) THEN
CALL OTHER_SUB(A,B)
ELSE
CALL OTHER_SUB(A)
ENDIF
...
END SUBROUTINE MAIN_SUB
In other words, OTHER_SUB is a subroutine which arguments are exactly as provided by MAIN_SUB.
The branching statement above works well when there is one optional argument. But what if my subroutine has, say, 4 or 5? It becomes a combinatorial problem to implement all the possible present/non present combinations for all optional arguments!
Am I doing something more complicated than necessary? Maybe there is another solution?
Thanks,
Olivier
SUBROUTINE MAIN_SUB(A,B)
USE INTERFACE_FOR_OTHER_SUB (module containing the interface definition for OTHER_SUB).
IMPLICIT NONE
INTEGER,INTENT(INOUT) :: A
INTEGER,INTENT(IN),OPTIONAL :: B
...
Assume that MAIN_SUB calls another subroutine for which the argument Bis also optional. I suppose I need to do the following:
...
IF (PRESENT(B)) THEN
CALL OTHER_SUB(A,B)
ELSE
CALL OTHER_SUB(A)
ENDIF
...
END SUBROUTINE MAIN_SUB
In other words, OTHER_SUB is a subroutine which arguments are exactly as provided by MAIN_SUB.
The branching statement above works well when there is one optional argument. But what if my subroutine has, say, 4 or 5? It becomes a combinatorial problem to implement all the possible present/non present combinations for all optional arguments!
Am I doing something more complicated than necessary? Maybe there is another solution?
Thanks,
Olivier
Unless you can make use ofdefault values. Something like :
integer :: local_b, local_c
local_b = 42
local_c = 9
if (present(b)) local_b = b
if (present(c)) local_c = c
call other_sub(a,local_b,local_c)
etc
Otherwise you're stuck with the multiple if/then tests
Les
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello Olivier,
To avoid the many combinations of variables in the other_sub() call statement, perhaps you could use one or two small arrays? When I have a routine with many possible variables being passed, I will put the values into a small array and then pass that array to the other_sub(). Another possibility is to use a derived data type that contains all the variable values that you would like to pass. I find both the approaches very helpful when over time more variables are added. The array or data type can be easily expanded, and no changes to the call statement are needed, which is very convenient.
For example using a few small arrays:
integer, parameter :: array_size = 10 ! set the size you need
integer :: values_int(array_size)
real :: values_real(array_size)
! then set your variables into the small arrays
values_int(1) = A
...
values_real(1) = B
...
! then call the subroutine
call other_sub(array_size,values_int,values_real)
Or for example with a derived data type:
! declare this in your routine, or in a module
type my_type
integer :: a
integer :: b
real :: c
end type
! declare a variable of my_type in your routine
my_type :: values
values%a = A
values%b = B
values%c = C
! then call your subroutine
call other_sub(values)
Would either of these approaches help with the many variable cases needed? I think the syntax to set up either approach is easier than a big if-block, and can be easily expanded without needing to change the call statement.
Regards,
Greg
To avoid the many combinations of variables in the other_sub() call statement, perhaps you could use one or two small arrays? When I have a routine with many possible variables being passed, I will put the values into a small array and then pass that array to the other_sub(). Another possibility is to use a derived data type that contains all the variable values that you would like to pass. I find both the approaches very helpful when over time more variables are added. The array or data type can be easily expanded, and no changes to the call statement are needed, which is very convenient.
For example using a few small arrays:
integer, parameter :: array_size = 10 ! set the size you need
integer :: values_int(array_size)
real :: values_real(array_size)
! then set your variables into the small arrays
values_int(1) = A
...
values_real(1) = B
...
! then call the subroutine
call other_sub(array_size,values_int,values_real)
Or for example with a derived data type:
! declare this in your routine, or in a module
type my_type
integer :: a
integer :: b
real :: c
end type
! declare a variable of my_type in your routine
my_type :: values
values%a = A
values%b = B
values%c = C
! then call your subroutine
call other_sub(values)
Would either of these approaches help with the many variable cases needed? I think the syntax to set up either approach is easier than a big if-block, and can be easily expanded without needing to change the call statement.
Regards,
Greg
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks to all for such detailed answers!! Your suggestions gave me the following idea: create a derived type in which all the optional arguments are listedalong a logical flag (indicating that the derived type component variables are active/present)is provided for each argument.
TYPE MY_OPTIONAL_ARGS
INTEGER A,B,C,D
LOGICAL A_IS_PRESENT,B_IS_PRESENT,C_IS_PRESENT,D_IS_PRESENT
END TYPE MY_OPTIONAL_ARGS
But this kind of defeats the very purpose of optional arguments of course.
Well... as long as it works and gets the job done...
Thanks again,
Olivier
TYPE MY_OPTIONAL_ARGS
INTEGER A,B,C,D
LOGICAL A_IS_PRESENT,B_IS_PRESENT,C_IS_PRESENT,D_IS_PRESENT
END TYPE MY_OPTIONAL_ARGS
But this kind of defeats the very purpose of optional arguments of course.
Well... as long as it works and gets the job done...
Thanks again,
Olivier
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Olivier,
I believe that an incomming optional argument(s) can be passed as optional argumentsin a call to a nested subroutine as long as it is (they are) last in the argument list of the nested subroutine. This should be easy enough for you to test.
Have you looked at using generic interfaces? Depending on the amount of common code you might find it best to write two subroutines with one generic name.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quoting - jimdempseyatthecove
I believe that an incomming optional argument(s) can be passed as optional argumentsin a call to a nested subroutine as long as it is (they are) last in the argument list of the nested subroutine.
Order in the nested subroutine argument list doesn't matter, the 'not present'ness of the original argument is passed along regardless, as long as the receiving dummy arguments are also OPTIONAL (12.4.1.6 in F2003, and it is in a Dr Fortran article on optional arguments too).
[cpp]! $Id:$ ! Test of optionality MODULE AnOptionalTest IMPLICIT NONE CONTAINS SUBROUTINE TopLevel(top_arg1, top_arg2, top_arg3) CHARACTER(*), OPTIONAL :: top_arg1, top_arg2, top_arg3 !**** ! Just pass through CALL BottomLevel(top_arg1, top_arg2, top_arg3) END SUBROUTINE TopLevel SUBROUTINE BottomLevel(bot_arg1, bot_arg2, bot_arg3) CHARACTER(*), OPTIONAL :: bot_arg1, bot_arg2, bot_arg3 !**** CALL AmIThere(bot_arg1, 'bot_arg1') CALL AmIThere(bot_arg2, 'bot_arg2') CALL AmIThere(bot_arg3, 'bot_arg3') END SUBROUTINE BottomLevel SUBROUTINE AmIThere(arg, str) CHARACTER(*) :: str CHARACTER(*), OPTIONAL :: arg !**** IF (PRESENT(arg)) THEN WRITE (*,100) str, arg ELSE WRITE (*,110) str END IF 100 FORMAT(A,' is there with value ',A) 110 FORMAT(A,' is not present') END SUBROUTINE AmIThere END MODULE AnOptionalTest PROGRAM ItsAllOptional USE AnOptionalTest IMPLICIT NONE !**** CALL TopLevel(top_arg2='Hi there') END PROGRAM ItsAllOptional[/cpp]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Good advice all around. Just don't forget that an explicit interface is required to be visible to the caller if the routine has an OPTIONAL argument. Here's the link to Doctor Fortran and the Virtues of Omission.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Oooh, this is perfect."Presence Optionality" (for lack of a better term)of an argumentis passed to subroutines, as long as there is an explicit interface for these subroutines.
That's going to save me a lot of headaches...
Thank you!!
Olivier
That's going to save me a lot of headaches...
Thank you!!
Olivier
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As far as I know, I guess that missing optional argument pointer are set to 0 by the caller and the IS_PRESENT function only test if the pointer value is 0 or not.
So if it is true, optional arguments can be transfered automatically to other subroutine with optional arguments.
So if it is true, optional arguments can be transfered automatically to other subroutine with optional arguments.
Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page