Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29392 Discussions

Optional argument with default value

GVautier
New Contributor III
3,670 Views

What will be the best solution to affect default value to an optional argument?

subroutine dummy(logical_arg)
logical,intent(in),optional :: logical_arg

logical_value=.false.
if( present(logical_arg)) logical_value=logical_arg
....
if( logical_value) then
...
endif
..
end subroutine

or create a function get_arg_logical (and procedure get_arg_value for all kinds of arguments)

logical function get_arg_logical(default_value,logical_arg)
logical,intent(in),optional :: logical_arg
logical,intent(in) :: default_value

get_arg_logical=default_value
if( present(logical_arg)) get_arg_logical=logical_arg
end function

and use get_arg_value(.false.,logical_arg) and so on where needed

subroutine dummy(logical_arg)

logical,intent(in),optional :: logical_arg

....
if(  get_arg_value(.false.,logical_arg) ) then
...
endif
..
end subroutine


Thanks for your suggestions

0 Kudos
7 Replies
Arjen_Markus
Honored Contributor II
3,670 Views

The second solution won't work: the "presentness" of an argument is not passed on to a subroutine.

An alternative to using optional arguments is the use of overloaded subroutines:

interface my_dummy
   subroutine my_dummy_no_arg
   subroutine my_dummy_with_arg
end interface my_dummy

subroutine my_dummy_no_arg
    call my_dummy_with_arg( .false. )
end subroutine my_dummy_no_arg

subroutine my_dummy_with_arg
    ....
end subroutine my_dummy_with_arg

Of course just a sketch and it gets awkward with more than a few cases.

0 Kudos
mecej4
Honored Contributor III
3,670 Views

arjenmarkus wrote:
the "presentness" of an argument is not passed on to a subroutine

If that were true, the usefulness of optional arguments would be substantially reduced.

However, the F2008 standard says in Section 12.5.2.12, Note 4: "Except as noted in the list above, it may be supplied as an actual argument corresponding to an optional dummy argument, which is then also considered not to be present" where "it" refers to an optional dummy argument that is not present. The restrictions are listed in Note 3 of the same section, and I do not see that any of them are violated in the example code.

Here is a complete test program to check this out.

program passoptl

   call dummy(.true.)
   call dummy(.false.)
   call dummy()

contains

logical function get_arg_value(default_value,logical_arg)
   implicit none
   logical,intent(in),optional :: logical_arg
   logical,intent(in) :: default_value

   get_arg_value=default_value
   if( present(logical_arg)) get_arg_value=logical_arg
end function get_arg_value

subroutine dummy(logical_arg)
   implicit none
   logical,intent(in),optional :: logical_arg

   write(*,*)'Entered dummy()'
   if(  get_arg_value(.false.,logical_arg) ) then
      write(*,*)'Branch false true'
   else 
      write(*,*)'Branch false false'   
   endif
   if(  get_arg_value(.true.,logical_arg) ) then
      write(*,*)'Branch true true'
   else
      write(*,*)'Branch true false'   
   endif
end subroutine dummy

end program passoptl

 

0 Kudos
Arjen_Markus
Honored Contributor II
3,670 Views

Interesting, I was not aware of this.

0 Kudos
mecej4
Honored Contributor III
3,670 Views

I was not aware, either, and threads like this are valuable because they raise questions that make one read the standard carefully. The list of exceptions in Section 12.5.2.12, Note 3 is too long and the rules too complicated for me to remember, so I often fall back on asking myself if any quoted/misquoted rule would severely reduce the usefulness of the language. Such rules would have caused vendors and users to make strong objections to the standards committee, so a rereading of the relevant sections of the standard was indicated.

If passing along optional dummy arguments that are not present as actual arguments did not transmit the PRESENT status with the variables, it would have become necessary to have long chains of IF(PRESENT(...))THEN... blocks with all the possible combinations of the optional variables spelled out.

Reply to O.P.: I do not think that the overhead of using an additional function is justified, unless there are lots of instances of such assignments. The use of the literal constant .FALSE. as an actual argument in the second alternative makes the code less readable. If you modify the first alternative slightly as

IF( present(logical_arg)) THEN
   logical_value=logical_arg
ELSE
   logical_value=.false.
ENDIF

the code will be clearer. Let the compiler decide whether speculative assignment should be done.

0 Kudos
GVautier
New Contributor III
3,670 Views

Thanks for your contributions.

In my mind, if an argument is not present, the value of it's pointer in the call stack is null. The PRESENT function only check if the pointer is null so it can be transmitted to inner calls.

That's why assigning a value to a non present argument crashes the app.

Is it right?

The solution with the function could make easier to search where default values are used.

0 Kudos
mecej4
Honored Contributor III
3,670 Views

Whether an optional argument that is not present is represented by a null pointer is an implementation detail that can change from compiler to compiler. Note that the processor need not even have a stack and still function perfectly well with compiled Fortran code. Another related question would relate to how to distinguish between optional arguments that are not present from pointer arguments that are present but are not yet associated.

If you wish to use the function alternative, it would be better to use a logical variable, say, "IsPresent", instead of the literal value .FALSE., for the sake of readability.

0 Kudos
IanH
Honored Contributor III
3,670 Views

The pattern I tend to use for leaf procedures (procedures that aren't going to be passing the optional argument on to other procedures that also have a corresponding optional argument) is something like this:

SUBROUTINE sub_with_optional(..., arg)
  LOGICAL, INTENT(IN), OPTIONAL :: arg
  
  LOGICAL, PARAMETER :: default_arg = .FALSE.  ! set accordingly
  LOGICAL :: local_arg
  
  IF (PRESENT(arg)) THEN
    local_arg = arg
  ELSE
    local_arg = default_arg
  END IF

  some_variable = an_expression .involving. local_arg
  CALL do_something_with(..., local_arg, etc)
END SUBROUTINE sub_with_optional

 

0 Kudos
Reply